第25章. 列表结构

"与列表(and list)""或列表(or list)" 结构提供一种处理一串连续命令的方法. 它们能有效地替代复杂的嵌套if/then语句甚至可以代替case语句.

连接命令

与列表(and list)

   1 command-1 && command-2 && command-3 && ... command-n
如果每个命令都返回真值(0)将会依次执行下去. 当某个命令返回假值(非零值), 整个命令链就会结束执行(第一个返回假的命令将会是最后一个执行的命令,后面的都不再执行).


例子 25-1. 使用"与列表(and list)"来测试命令行参数

   1 #!/bin/bash
   2 # "and list"
   3 
   4 if [ ! -z "$1" ] && echo "Argument #1 = $1" && [ ! -z "$2" ] && echo "Argument #2 = $2"
   5 then
   6   echo "At least 2 arguments passed to script."
   7   # 所有连接起来的命令都返回真.
   8 else
   9   echo "Less than 2 arguments passed to script."
  10   # 整个命令列表中至少有一个命令返回假值.
  11 fi  
  12 # 注意"if [ ! -z $1 ]" 可以工作,但它是有所假定的等价物,
  13 #   if [ -n $1 ] 不会工作.
  14 #     但是, 加引用可以让它工作.
  15 #  if [ -n "$1" ] 就可以了.
  16 #     小心!
  17 # 最好总是引起要测试的变量.
  18 
  19 
  20 # 这是使用"纯粹"的 if/then 语句完成的同等功能.
  21 if [ ! -z "$1" ]
  22 then
  23   echo "Argument #1 = $1"
  24 fi
  25 if [ ! -z "$2" ]
  26 then
  27   echo "Argument #2 = $2"
  28   echo "At least 2 arguments passed to script."
  29 else
  30   echo "Less than 2 arguments passed to script."
  31 fi
  32 # 这会更长且不如"与列表"精致.
  33 
  34 
  35 exit 0


例子 25-2. 用"与列表"的另一个命令行参数测试

   1 #!/bin/bash
   2 
   3 ARGS=1        # 期望的参数个数.
   4 E_BADARGS=65  # 如果用户给出不正确的参数个数的退出码.
   5 
   6 test $# -ne $ARGS && echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_BADARGS
   7 #  如果 条件1 测试为真(表示传给脚本的参数不对),
   8 #+ 则余下的命令会被执行,并且脚本结束运行.
   9 
  10 # 下面的代码只有当上面的测试失败时才会执行.
  11 echo "Correct number of arguments passed to this script."
  12 
  13 exit 0
  14 
  15 # 为了检查退出码,脚本结束后用"echo $?"来查看退出码.

当然,一个与列表也能给变量设置默认值.
   1 arg1=$@       # 不管怎样,设置变量$arg1为命令行参数.
   2 
   3 [ -z "$arg1" ] && arg1=DEFAULT
   4               # 如果没有在命令行上指定参数则把$arg1设置为DEFAULT.

或列表(or list)

   1 command-1 || command-2 || command-3 || ... command-n
只要前一个命令返回假命令链就会依次执行下去. 一旦有一个命令返回真, 命令链就会结束(第一个返回真的命令将会是最后一个执行的命令). 这显然和"与列表"正好相反.


例子 25-3. "或列表""与列表"的结合使用

   1 #!/bin/bash
   2 
   3 #  delete.sh, 不是很聪明的文件删除功能.
   4 #  用法: delete filename
   5 
   6 E_BADARGS=65
   7 
   8 if [ -z "$1" ]
   9 then
  10   echo "Usage: `basename $0` filename"
  11   exit $E_BADARGS  # 没有参数? 跳出脚本.
  12 else  
  13   file=$1          # 设置文件名.
  14 fi  
  15 
  16 
  17 [ ! -f "$file" ] && echo "File \"$file\" not found. \
  18 Cowardly refusing to delete a nonexistent file."
  19 # 与列表, 用于文件不存在时给出一个错误信息.
  20 # 注意 echo 命令的参数用了一个转义符继续使第二行也是这个命令的参数.
  21 
  22 [ ! -f "$file" ] || (rm -f $file; echo "File \"$file\" deleted.")
  23 # 或列表, 用于存在文件时删除此文件.
  24 
  25 # 注意上面两个相反的逻辑.
  26 # 与列表为真时才执行, 或列表为假时执行.
  27 
  28 exit 0

Caution

如果在与列表的第一个命令返回真时,它会执行.

   1 # ==> 下面的片断摘自Miquel van Smoorenburg写的 /etc/rc.d/init.d/single 脚本 
   2 #+==> 示例与和或列表的使用.
   3 # ==> "箭头"的注释由本书作者添加.
   4 
   5 [ -x /usr/bin/clear ] && /usr/bin/clear
   6   # ==> 如果 /usr/bin/clear 存在, 则调用它.
   7   # ==> 在调用一个命令前检查它是否存在,
   8   #+==> 以避免产生错误信息和其他难读懂的结果.
   9 
  10   # ==> . . .
  11 
  12 # 如果他们想在单用户模式下运行某些程序, 可能也会运行这个...
  13 for i in /etc/rc1.d/S[0-9][0-9]* ; do
  14         # 检查脚本是否可执行.
  15         [ -x "$i" ] || continue
  16   # ==> 如果在目录$PWD中相应的文件没有发现,
  17   #+==> 则会跳过此次循环.
  18 
  19         # 不接受备份文件和由rpm产生的文件.
  20         case "$1" in
  21                 *.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)
  22                         continue;;
  23         esac
  24         [ "$i" = "/etc/rc1.d/S00single" ] && continue
  25   # ==> 设置脚本名,但还不执行它.
  26         $i start
  27 done
  28 
  29   # ==> . . .

Important

与列表或是或列表退出状态是最后一个执行命令的退出状态.

灵活地组合"与"和"或"列表是允许的,但这样逻辑会很容易变得费解并且需要较多的测试.
   1 false && true || echo false         # false
   2 
   3 # 结果等同
   4 ( false && true ) || echo false     # false
   5 # 但不同与
   6 false && ( true || echo false )     # (没有输出)
   7 
   8 #  注意是从左到右来分组并求值的,
   9 #+ 因为逻辑操作符"&&"和"||"有相同的优先处理权.
  10 
  11 #  最好避免这种复杂,除非你确实知道你在做什么.
  12 
  13 #  Thanks, S.C.

参考例子 A-7例子 7-4 演示的使用与/或列表测试变量的例子.