if/then结构用来判断命令列表的退出状态码是否为0(因为在UNIX惯例, 0表示"成功"), 如果成功的话, 那么就执行接下来的一个或多个命令.
有一个专有命令[ (左中括号, 特殊字符). 这个命令与test命令等价, 并且出于效率上的考虑, 这是一个内建命令. 这个命令把它的参数作为比较表达式或者作为文件测试, 并且根据比较的结果来返回一个退出状态码(0 表示真, 1表示假).
在版本2.02的Bash中, 引入了[[ ... ]]扩展测试命令, 因为这种表现形式可能对某些语言的程序员来说更容易熟悉一些. 注意[[是一个关键字, 并不是一个命令.
Bash把[[ $a -lt $b ]]看作一个单独的元素, 并且返回一个退出状态码.
(( ... ))和let ...结构也能够返回退出状态码, 当它们所测试的算术表达式的结果为非零的时候, 将会返回退出状态码0. 这些算术扩展结构被用来做算术比较.
1 let "1<2" returns 0 (as "1<2" expands to "1") 2 (( 0 && 1 )) returns 1 (as "0 && 1" expands to "0") |
if命令能够测试任何命令, 并不仅仅是中括号中的条件.
1 if cmp a b &> /dev/null # 禁止输出. 2 then echo "Files a and b are identical." 3 else echo "Files a and b differ." 4 fi 5 6 # 非常有用的"if-grep"结构: 7 # ------------------------ 8 if grep -q Bash file 9 then echo "File contains at least one occurrence of Bash." 10 fi 11 12 word=Linux 13 letter_sequence=inu 14 if echo "$word" | grep -q "$letter_sequence" 15 # "-q" 选项是用来禁止输出的. 16 then 17 echo "$letter_sequence found in $word" 18 else 19 echo "$letter_sequence not found in $word" 20 fi 21 22 23 if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED 24 then echo "Command succeeded." 25 else echo "Command failed." 26 fi |
一个if/then结构可以包含嵌套的比较操作和条件判断操作.
1 if echo "Next *if* is part of the comparison for the first *if*." 2 3 if [[ $comparison = "integer" ]] 4 then (( a < b )) 5 else 6 [[ $a < $b ]] 7 fi 8 9 then 10 echo '$a is less than $b' 11 fi |
谦虚的Stephane Chazelas解释了"if-test"的详细细节.
例子 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 |
如果if和then在条件判断的同一行上的话, 必须使用分号来结束if表达式. if和then都是关键字. 关键字(或者命令)如果作为表达式的开头, 并且如果想在同一行上再写一个新的表达式的话, 那么必须使用分号来结束上一句表达式.
|
elif是else 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的新版本中必须要求使用.
test命令在Bash中是内建命令, 用来测试文件类型, 或者用来比较字符串. 因此, 在Bash脚本中, test命令并不会调用外部的/usr/bin/test中的test命令, 这是sh-utils工具包中的一部分. 同样的, [也并不会调用/usr/bin/[, 这是/usr/bin/test的符号链接.
|
例子 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中引进的.
在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割, 但是会发生参数扩展和命令替换. |
1 file=/etc/passwd 2 3 if [[ -e $file ]] 4 then 5 echo "Password file exists." 6 fi |
使用[[ ... ]]条件判断结构, 而不是[ ... ], 能够防止脚本中的许多逻辑错误. 比如, &&, ||, <, 和> 操作符能够正常存在于[[ ]]条件判断结构中, 但是如果出现在[ ]结构中的话, 会报错. |
在if后面也不一定非得是test命令或者是用于条件判断的中括号结构( [ ] 或 [[ ]] ).
与此相似, 在中括号中的条件判断也不一定非得要if不可, 也可以使用列表结构.
|
(( ))结构扩展并计算一个算术表达式的值. 如果表达式的结果为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 |