全部博文(584)
分类: LINUX
2010-01-06 15:03:19
摘自《高级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