分类:
2010-01-03 22:27:44
摘自《高级bash脚本编程指南》
退出状态和退出状态码
exit 被用来结束一个脚本, 就像在C语言中一样. 它也返回一个值, 并且这个值会传递给脚本的父进程, 父进程会使用这个值做下一步的处理.
每个命令都会返回一个 退出状态码 (有时候也被称为 返回状态 ). 成功的命令返回0, 而不成功的命令返回非零值, 非零值通常都被解释成一个错误码. 行为良好的UNIX命令, 程序, 和工具都会返回0作为退出码来表示成功, 虽然偶尔也会有例外.
同样的, 脚本中的函数和脚本本身也会返回退出状态码. 在脚本或者是脚本函数中执行的最后的命令会决定退出状态码. 在脚本中, exit nnn命令将会把 nnn退出码传递给shell( nnn必须是十进制数, 范围必须是0 – 255).
当脚本以不带参数的exit命令来结束时, 脚本的退出状态码就由脚本中最后执行的命令来决定(就是exit之前的命令).
1 #!/bin/bash 2 3 COMMAND_1 4 5 . . . 6 7 # 将以最后的命令来决定退出状态码. 8 COMMAND_LAST 9 10 exit
不带参数的exit命令与 exit $? 的效果是一样的, 甚至脚本的结尾不写exit, 也与前两者的效果相同.
$? 保存了最后所执行的命令的退出状态码. 当函数返回之后, $?保存函数中最后所执行的命令的退出状态码. 这就是bash对函数”返回值”的处理方法. 当一个脚本退出, $?保存了脚本的退出状态码, 这个退出状态码也就是脚本中最后一个执行命令的退出状态码. 一般情况下, 0表示成功, 在范围1 – 255的整数表示错误.
$? 用于测试脚本中的命令结果的时候, 往往显得特别有用。
! 逻辑 “非”操作符, 将会反转命令或条件测试的结果, 并且这会影响退出状态码.
例:反转一个条件的用法!
1 true # "true" 是内建命令. 2 echo "exit status of \"true\" = $?" # 0 3 4 ! true 5 echo "exit status of \"! true\" = $?" # 1 6 # 注意: "!" 需要一个空格. 7 # !true 将导致"command not found"错误 8 # 9 # 如果一个命令以'!'开头, 那么会启用Bash的历史机制. 10 11 true 12 !true 13 # 这次就没有错误了, 也没有反转结果. 14 # 它只是重复了之前的命令(true). 15 16 # 感谢, Stephane Chazelas和Kristopher Newsome.
条件测试结构
(( … )) 和 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
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 都是关键字. 关键字(或者命令)如果作为表达式的开头, 并且如果想在同一行上再写一个新的表达式的话, 那么必须使用分号来结束上一句表达式.
1 if [ -x "$filename" ]; then
Else if和elif
elif
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 ] (if [ condition ] 判断语句中要在condition与中括号之间加入空格)完全相同. 就像我们前面所看到的, 左中括号, [ , 是调用test命令的标识. 而关闭条件判断用的是右中括号, ], 在if/test结构中并不是严格必需的, 但是在Bash的新版本中必须要求使用.
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
[[ ]]结构比[ ]结构更加通用. 这是一个扩展的test命令
在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割, 但是会发生参数扩展和命令替换.
1 file=/etc/passwd 2 3 if [[ -e $file ]] 4 then 5 echo "Password file exists." 6 fi
使用[[ ... ]]条件判断结构, 而不是[ ... ], 能够防止脚本中的许多逻辑错误. 比如, &&, ||, <, 和> 操作符能够正常存在于[[ ]]条件判断结构中, 但是如果出现在[ ]结构中的话, 会报错.
在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 根据 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 命令和 [ ] 结构的行为正好相反.
文件测试操作符
如果下面的条件成立将会返回真.
-e 文件存在 -a 文件存在。这个选项的效果与-e相同. 但是它已经被"弃用"了, 并且不鼓励使用. -f 表示这个文件是一个一般文件(并不是目录或者设备文件) -s 文件大小不为零 -d 表示这是一个目录 -b 表示这是一个块设备(软盘, 光驱, 等等.) -c 表示这是一个字符设备(键盘, modem, 声卡, 等等.) -p 这个文件是一个管道 -h 这是一个符号链接 -L 这是一个符号链接 -S 表示这是一个socket -t 文件(描述符)被关联到一个终端设备上。 -r 文件是否具有可读权限(指的是正在运行这个测试命令的用户是否具有读权限) -w 文件是否具有可写权限(指的是正在运行这个测试命令的用户是否具有写权限) -x 文件是否具有可执行权限(指的是正在运行这个测试命令的用户是否具有可执行权限) -g set-group-id(sgid)标记被设置到文件或目录上 -u set-user-id (suid)标记被设置到文件上 -k 设置粘贴位 -O 判断你是否是文件的拥有者 -G 文件的group-id是否与你的相同 -N 从文件上一次被读取到现在为止, 文件是否被修改过 f1 -nt f2 文件f1比文件f2新 f1 -ot f2 文件f1比文件f2旧 f1 -ef f2 文件f1和文件f2是相同文件的硬链接 ! "非" -- 反转上边所有测试的结果(如果没给出条件, 那么返回真).
其他比较操作符
二元比较操作符用来比较两个变量或数字. 注意整数比较与字符串比较的区别.
整数比较
-eq 等于 if [ "$a" -eq "$b" ] -ne 不等于 if [ "$a" -ne "$b" ] -gt 大于 if [ "$a" -gt "$b" ] -ge 大于等于 if [ "$a" -ge "$b" ] -lt 小于 if [ "$a" -lt "$b" ] -le 小于等于 if [ "$a" -le "$b" ] < 小于(在双括号中使用) (("$a" < "$b")) <= 小于等于(在双括号中使用) (("$a" <= "$b")) > 大于(在双括号中使用) (("$a" > "$b")) >= 大于等于(在双括号中使用) (("$a" >= "$b"))
字符串比较
= 等于 if [ "$a" = "$b" ] == 等于 if [ "$a" == "$b" ] 与=等价. ==比较操作符在双中括号对和单中括号对中的行为是不同的.1 [[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么结果将为真 2 [[ $a == "z*" ]] # 如果$a与z*相等(就是字面意思完全一样), 那么结果为真. 3 4 [ $a == z* ] # 文件扩展匹配(file globbing)和单词分割有效. 5 [ "$a" == "z*" ] # 如果$a与z*相等(就是字面意思完全一样), 那么结果为真. 6 7 # 感谢, Stephane Chazelas!= 不等号
if [ "$a" != "$b" ]
这个操作符将在[[ ... ]]结构中使用模式匹配.
< 小于, 按照ASCII字符进行排序
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
注意"<"使用在[ ]结构中的时候需要被转义.
> 大于, 按照ASCII字符进行排序
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
注意">"使用在[ ]结构中的时候需要被转义.
-z 字符串为"null", 意思就是字符串长度为零
-n 字符串不为"null".
当 -n 使用在中括号中进行条件测试的时候, 必须要把字符串用双引号引用起来. 如果采用了未引用的字符串来使用! -z, 甚至是在条件测试中括号只使用未引用的字符串的话, 一般也是可以工作的, 然而, 这是一种不安全的习惯. 习惯于使用引用的测试字符串才是正路. 就像S.C.所指出的那样, 在一个混合测试中, 即使使用引用的字符串变量也可能还不够. 如果$string为空的话, [ -n "$string" -o "$a" = "$b" ]可能会在某些版本的Bash中产生错误. 安全的做法是附加一个额外的字符给可能的空变量, [ "x$string" != x -o "x$a" = "x$b" ] (“x”字符是可以相互抵消的).
compound comparison
-a 逻辑与
exp1 -a exp2 如果表达式exp1和exp2都为真的话, 那么结果为真.
-o 逻辑或
exp1 -o exp2 如果表达式exp1和exp2中至少有一个为真的话, 那么结果为真.
这与Bash中的比较操作符&&和||非常相像, 但是这个两个操作符是用在双中括号结构中的.
[[ condition1 && condition2 ]]
-o 和 -a 操作符一般都是和 test 命令或者是单中括号结构一起使用的.
if [ "$exp1" -a "$exp2" ]
嵌套的if/then条件测试
可以通过if/then结构来使用嵌套的条件测试. 最终的结果和上面使用 && 混合比较操作符的结果是相同的.
1 if [ condition1 ] 2 then 3 if [ condition2 ] 4 then 5 do-something # But only if both "condition1" and "condition2" valid. 6 fi 7 fi