7.1. 条件测试结构


例子 7-1. 什么是真?

  1 #!/bin/bash
  2 
  3 #  小技巧:
  4 #  如果你不能够确定一个特定的条件该如何进行判断,
  5 #+ 那么就使用if-test结构. 
  6 
  7 echo
  8 
  9 echo "Testing \"0\""
 10 if [ 0 ]      # zero
 11 then
 12   echo "0 is true."
 13 else
 14   echo "0 is false."
 15 fi            # 0 为真.
 16 
 17 echo
 18 
 19 echo "Testing \"1\""
 20 if [ 1 ]      # one
 21 then
 22   echo "1 is true."
 23 else
 24   echo "1 is false."
 25 fi            # 1 为真.
 26 
 27 echo
 28 
 29 echo "Testing \"-1\""
 30 if [ -1 ]     # 负1
 31 then
 32   echo "-1 is true."
 33 else
 34   echo "-1 is false."
 35 fi            # -1 为真.
 36 
 37 echo
 38 
 39 echo "Testing \"NULL\""
 40 if [ ]        # NULL (空状态)
 41 then
 42   echo "NULL is true."
 43 else
 44   echo "NULL is false."
 45 fi            # NULL 为假.
 46 
 47 echo
 48 
 49 echo "Testing \"xyz\""
 50 if [ xyz ]    # 字符串
 51 then
 52   echo "Random string is true."
 53 else
 54   echo "Random string is false."
 55 fi            # 随便的一串字符为真.
 56 
 57 echo
 58 
 59 echo "Testing \"\$xyz\""
 60 if [ $xyz ]   # 判断$xyz是否为null, 但是...
 61               # 这只是一个未初始化的变量.
 62 then
 63   echo "Uninitialized variable is true."
 64 else
 65   echo "Uninitialized variable is false."
 66 fi            # 未定义的初始化为假.
 67 
 68 echo
 69 
 70 echo "Testing \"-n \$xyz\""
 71 if [ -n "$xyz" ]            # 更加正规的条件检查.
 72 then
 73   echo "Uninitialized variable is true."
 74 else
 75   echo "Uninitialized variable is false."
 76 fi            # 未初始化的变量为假.
 77 
 78 echo
 79 
 80 
 81 xyz=          # 初始化了, 但是赋null值.
 82 
 83 echo "Testing \"-n \$xyz\""
 84 if [ -n "$xyz" ]
 85 then
 86   echo "Null variable is true."
 87 else
 88   echo "Null variable is false."
 89 fi            # null变量为假. 
 90 
 91 
 92 echo
 93 
 94 
 95 # 什么时候"false"为真?
 96 
 97 echo "Testing \"false\""
 98 if [ "false" ]              #  看起来"false"只不过是一个字符串而已. 
 99 then
100   echo "\"false\" is true." #+ 并且条件判断的结果为真.
101 else
102   echo "\"false\" is false."
103 fi            # "false" 为真.
104 
105 echo
106 
107 echo "Testing \"\$false\""  # 再来一个, 未初始化的变量.
108 if [ "$false" ]
109 then
110   echo "\"\$false\" is true."
111 else
112   echo "\"\$false\" is false."
113 fi            # "$false" 为假.
114               # 现在, 我们得到了预期的结果.
115 
116 #  如果我们测试以下为初始化的变量"$true"会发生什么呢?
117 
118 echo
119 
120 exit 0

练习. 解释上边的例子 7-1的行为.

  1 if [ condition-true ]
  2 then
  3    command 1
  4    command 2
  5    ...
  6 else
  7    # 可选的(如果不需要可以省去).
  8    # 如果原始的条件判断的结果为假, 那么在这里添加默认的代码块来执行.
  9    command 3
 10    command 4
 11    ...
 12 fi

Note

如果ifthen在条件判断的同一行上的话, 必须使用分号来结束if表达式. ifthen都是关键字. 关键字(或者命令)如果作为表达式的开头, 并且如果想在同一行上再写一个新的表达式的话, 那么必须使用分号来结束上一句表达式.

  1 if [ -x "$filename" ]; then

Else if和elif

elif

elifelse if的缩写形式. 作用是在外部的判断结构中再嵌入一个内部的if/then结构.

  1 if [ condition1 ]
  2 then
  3    command1
  4    command2
  5    command3
  6 elif [ condition2 ]
  7 # 与else if一样
  8 then
  9    command4
 10    command5
 11 else
 12    default-command
 13 fi

if test condition-true结构与if [ condition-true ]完全相同. 就像我们前面所看到的, 左中括号, [ , 是调用test命令的标识. 而关闭条件判断用的的右中括号, ], 在if/test结构中并不是严格必需的, 但是在Bash的新版本中必须要求使用.

Note

test命令在Bash中是内建命令, 用来测试文件类型, 或者用来比较字符串. 因此, 在Bash脚本中, test命令并不会调用外部的/usr/bin/test中的test命令, 这是sh-utils工具包中的一部分. 同样的, [也并不会调用/usr/bin/[, 这是/usr/bin/test的符号链接.

bash$ type test
test is a shell builtin
bash$ type '['
[ is a shell builtin
bash$ type '[['
[[ is a shell keyword
bash$ type ']]'
]] is a shell keyword
bash$ type ']'
bash: type: ]: not found
	      


例子 7-2. test, /usr/bin/test, [ ], 和/usr/bin/[都是等价命令

  1 #!/bin/bash
  2 
  3 echo
  4 
  5 if test -z "$1"
  6 then
  7   echo "No command-line arguments."
  8 else
  9   echo "First command-line argument is $1."
 10 fi
 11 
 12 echo
 13 
 14 if /usr/bin/test -z "$1"      # 与内建的"test"命令结果相同. 
 15 then
 16   echo "No command-line arguments."
 17 else
 18   echo "First command-line argument is $1."
 19 fi
 20 
 21 echo
 22 
 23 if [ -z "$1" ]                # 与上边的代码块作用相同.
 24 #   if [ -z "$1"                应该能够运行, 但是...
 25 #+  Bash报错, 提示缺少关闭条件测试的右中括号. 
 26 then
 27   echo "No command-line arguments."
 28 else
 29   echo "First command-line argument is $1."
 30 fi
 31 
 32 echo
 33 
 34 
 35 if /usr/bin/[ -z "$1" ]       # 再来一个, 与上边的代码块作用相同.
 36 # if /usr/bin/[ -z "$1"       # 能够工作, 但是还是给出一个错误消息.
 37 #                             # 注意:
 38 #                               在版本3.x的Bash中, 这个bug已经被修正了.
 39 then
 40   echo "No command-line arguments."
 41 else
 42   echo "First command-line argument is $1."
 43 fi
 44 
 45 echo
 46 
 47 exit 0

[[ ]]结构比[ ]结构更加通用. 这是一个扩展的test命令, 是从ksh88中引进的.

Note

[[]]之间所有的字符都不会发生文件名扩展或者单词分割, 但是会发生参数扩展和命令替换.

  1 file=/etc/passwd
  2 
  3 if [[ -e $file ]]
  4 then
  5   echo "Password file exists."
  6 fi

Tip

使用[[ ... ]]条件判断结构, 而不是[ ... ], 能够防止脚本中的许多逻辑错误. 比如, &&, ||, <, 和> 操作符能够正常存在于[[ ]]条件判断结构中, 但是如果出现在[ ]结构中的话, 会报错.

Note

if后面也不一定非得是test命令或者是用于条件判断的中括号结构( [ ] 或 [[ ]] ).
  1 dir=/home/bozo
  2 
  3 if cd "$dir" 2>/dev/null; then   # "2>/dev/null" 会隐藏错误信息.
  4   echo "Now in $dir."
  5 else
  6   echo "Can't change to $dir."
  7 fi
"if COMMAND"结构将会返回COMMAND的退出状态码.

与此相似, 在中括号中的条件判断也不一定非得要if不可, 也可以使用列表结构.
  1 var1=20
  2 var2=22
  3 [ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"
  4 
  5 home=/home/bozo
  6 [ -d "$home" ] || echo "$home directory does not exist."

(( ))结构扩展并计算一个算术表达式的值. 如果表达式的结果为0, 那么返回的退出状态码1, 或者是"假". 而一个非零值的表达式所返回的退出状态码将为0, 或者是"true". 这种情况和先前所讨论的test命令和[ ]结构的行为正好相反.


例子 7-3. 算术测试需要使用(( ))

  1 #!/bin/bash
  2 # 算术测试.
  3 
  4 # (( ... ))结构可以用来计算并测试算术表达式的结果. 
  5 # 退出状态将会与[ ... ]结构完全相反!
  6 
  7 (( 0 ))
  8 echo "Exit status of \"(( 0 ))\" is $?."         # 1
  9 
 10 (( 1 ))
 11 echo "Exit status of \"(( 1 ))\" is $?."         # 0
 12 
 13 (( 5 > 4 ))                                      # 真
 14 echo "Exit status of \"(( 5 > 4 ))\" is $?."     # 0
 15 
 16 (( 5 > 9 ))                                      # 假
 17 echo "Exit status of \"(( 5 > 9 ))\" is $?."     # 1
 18 
 19 (( 5 - 5 ))                                      # 0
 20 echo "Exit status of \"(( 5 - 5 ))\" is $?."     # 1
 21 
 22 (( 5 / 4 ))                                      # 除法也可以.
 23 echo "Exit status of \"(( 5 / 4 ))\" is $?."     # 0
 24 
 25 (( 1 / 2 ))                                      # 除法的计算结果 < 1.
 26 echo "Exit status of \"(( 1 / 2 ))\" is $?."     # 截取之后的结果为 0.
 27                                                  # 1
 28 
 29 (( 1 / 0 )) 2>/dev/null                          # 除数为0, 非法计算. 
 30 #           ^^^^^^^^^^^
 31 echo "Exit status of \"(( 1 / 0 ))\" is $?."     # 1
 32 
 33 # "2>/dev/null"起了什么作用?
 34 # 如果这句被删除会怎样?
 35 # 尝试删除这句, 然后在运行这个脚本. 
 36 
 37 exit 0