目录

Shell流程控制


if 语句


  1. if test-commands; then
  2. consequent-commands;
  3. [elif more-test-commands; then
  4. more-consequents;]
  5. [else alternate-consequents;]
  6. fi

test-commands 既可以是 test 测试或[]、[[]]测试,也可以是任何其它命令,test-commands 用于条件测试,它只判断命令的退出状态码是否为 0,为 0 则为 true。

例如:

  1. if [ "$a" ];then echo '$a' is not none;else echo '$a' undefined or empty;fi
  2. if [ ! -d ~/.ssh ];then
  3. mkdir ~/.ssh
  4. chown -R $USER.$USER ~/.ssh
  5. chmod 700 ~/.ssh
  6. fi
  7. if grep 'rumenz' /etc/passwd &>/dev/null;then
  8. echo 'User "rumenz" already exists...'
  9. elif grep 'rumenz' /etc/passwd &>/dev/null;then
  10. echo 'User "rumenz" already exists...'
  11. else
  12. echo 'you should create user,exit...'
  13. exit 1
  14. fi

case


case 常用于确定的分支判断。比如:

  1. while [ "$1" ];do
  2. case "$1" in
  3. start)
  4. echo start;;
  5. stop)
  6. echo stop;;
  7. restart)
  8. echo restart;;
  9. reload | force-reload)
  10. echo reload;;
  11. status)
  12. echo status;;
  13. *)
  14. echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}"
  15. exit 2
  16. esac
  17. done

case 用法基本要求:

  • 每个小分句中的 pattern 部分都使用括号『()』包围,只不过左括号『(』不是必须的
  • 每个小分句的 pattern 支持通配模式匹配,可使用『|』分隔多个通配模式,表示满足其中一个模式即可

    • 例如([yY]|[yY][eE][sS]])表示即可以输入单个字母的 y 或 Y,还可以输入 yes 三个字母的任意大小写格式
  • 最后一般会定义一个能匹配其它任意条件的默认分支,即(*)
  • 除最后一个分支外,每个分支都建议以;;结尾,;;结尾符号表示小分句执行完成后立即退出 case 语句

for 循环


有两种 for 循环结构:

  1. # 成员测试类语法
  2. for i [ [in [words …] ] ; ] do commands; done
  3. # C语言for语法
  4. for (( expr1;expr2;expr3 ));do cmd_list;done

成员测试类的 for 循环中,in 关键字后是默认使用空格分隔的一个或多个元素,for 循环时,每次从 in 关键字后面取一个元素并赋值给 i 变量。

例如:

  1. $ for i in 1 2 "3 4";do echo $i;done
  2. 1
  3. 2
  4. 3 4

如果省略 in words,则等价于in "$@",即迭代位置参数。例如:

  1. set -- a b c
  2. for i do echo $i;done
  3. for i;do echo $i;done

C 语言型的 for 语法中,expr1 是初始化语句,expr2 是循环终点条件判断语句,expr3 是每轮循环后执行的语句,一般用来更改条件判断相关的变量。

  1. for ((i=1;i<=3;++i));do echo $i;done
  2. 1
  3. 2
  4. 3

对于成员测试类的语法,两点需要注意:

  1. 命令行解析时,路径扩展的过程在单词分割过程之后
  2. 迭代的元素中包含了空白
  1. touch "aa aaa.txt"
  2. touch "bb bbb.txt"
  3. for i in *.txt;do echo $i;done
  4. for i in $(ls *.txt);do echo $i;done
  5. (IFS=$'\n';for i in $(ls *.txt);do echo $i;done)

现在记住结论,后面介绍命令行解析的时候再做解释。

while 循环


  1. while test_cmd_list;do cmd_list;done

while 循环,开始时会测试test_cmd_list,如果测试的退出状态码为 0,则执行一次循环体语句cmd_list,然后再测试test_cmd_list,一直循环,直到测试退出状态码非 0,循环退出。

例如:

  1. let i=1,sum=0;
  2. while [ $i -le 10 ];do
  3. let sum=sum+i
  4. let ++i
  5. done

还有 until 循环语句,但在 Shell 中用的很少。

while 循环经常会和 read 命令一起使用,read 是 Bash 的内置命令,可用来读取文件,通常会按行读取:每次读一行。

例如:

  1. cat /etc/fstab | while read line;do
  2. let num+=1
  3. echo $num: $line
  4. done

上面的命令行中,首先 cat 进程和 while 结构开始运行,while 结构中的 read 命令从标准输入中读取,也就是从管道中读取数据,每次读取一行,因为管道中最初没有数据,所以 read 命令被阻塞处于数据等待状态。当 cat 命令读完文件所有数据后,将数据放入到管道中,于是 read 命令从管道中每次读取一行并将所读行赋值给变量 line,然后执行循环体,然后继续循环,直到 read 读完所有数据,循环退出。

但注意,管道两边的命令默认是在子 Shell 中执行的,所以其设置的变量在命令执行完成后就消失。换句话说,在父 Shell 中无法访问这些变量。比如上面的 num 变量是在管道的 while 结构中设置的,除了在 while 中能访问该变量,其它任何地方都无法访问它。

如果想要访问 while 中赋值的变量,就不能使用管道。如果是直接从文件读取,可使用输入重定向,如果是读取命令产生的数据,可使用进程替换。

  1. while read line;do
  2. let num1+=1
  3. echo $num1: $line
  4. done </etc/fstab
  5. echo $num1
  6. while read line;do
  7. let num2+=1
  8. echo $num2: $line
  9. done < <(grep 'UUID' /etc/fstab)

select 选项选择


select 可提供选项给用户选择。

  1. select name [ in word ] ; do cmd_list ; done

in word部分就是展示给用户的各个选项,如果省略,则等价于in "$@"。当用户输入其所选择的项后,对应项的内容保存到 name 变量,用户输入的内容保存到 REPLY 变量中。

注:REPLY 变量一般是序号值,但用户可以不按常理出牌,随意输入,所以 REPLY 保存的不一定是序号。

另外,用户做出选择后 select 会执行相关命令,执行完命令后会再次让用户选择。所以,应该在命令尾部使用 break 命令来终止 select。

例如:

  1. select fname in cat dog sheep mouse;do
  2. echo your choice: \"$REPLY\) $fname\"
  3. break
  4. done
  5. 1) cat
  6. 2) dog
  7. 3) sheep
  8. 4) mouse
  9. #? 3
  10. your choice: "3) sheep" # 将输出序号3对应的内容

continue、break、return、exit


  1. exit [n]
  2. 退出当前shell,在脚本中应用则表示退出整个脚本。其中数值n表示退出状态码。
  3. break [n]
  4. 退出整个循环,包括forwhileuntilselect语句。其中数值n表示退出的循环层次。
  5. continue [n]
  6. 退出当前循环进入下一次循环。n表示继续执行向外退出n层的循环。默认n=1,表示继续当前层的下一循环,n=2表示继续上一层的下一循环。
  7. return [n]
  8. 退出整个函数。n表示函数的退出状态码。

唯一需要注意的是 return,它并非只能用于 function 内部,绝大多数人都有这样的误解。如果 return 用在 function 之外,但在 source 命令的执行过程中,则直接停止该执行操作,并返回给定状态码 n(如果未给定,则为 0)。如果 return 在 function 之外,且不在 source 的执行过程中,则这是一个错误用法。

为什么要让 return 单独作用于 source 命令?如果了解 source 的特性『在当前 shell 而非子 shell 执行指定脚本中的代码』的话,就能理解为什么会这样。

比如设计一个脚本,它可以在当前 Shell 命令行下激活几个代理相关的变量,还能注销这些代理变量:

  1. proxy="http://127.0.0.1:8080"
  2. function active_proxy() {
  3. export http_proxy=$proxy
  4. export https_proxy=$proxy
  5. export ftp_proxy=$proxy
  6. export no_proxy=localhost
  7. }
  8. case $1 in
  9. set) active_proxy;;
  10. unset) unset http_proxy https_proxy ftp_proxy no_proxy;;
  11. *) return 0
  12. esac

因为变量要在当前 Shell 下生效,所以应该使用 source 命令去执行脚本:

  1. source proxy.sh set
  2. source proxy.sh unset
原文链接:https://rumenz.com/shell/shell-process.html
↑回到顶部↑
入门小站 @2018