Chinaunix首页 | 论坛 | 博客
  • 博客访问: 503178
  • 博文数量: 119
  • 博客积分: 5054
  • 博客等级: 大校
  • 技术积分: 1305
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-03 13:13
文章分类

全部博文(119)

文章存档

2011年(4)

2010年(115)

我的朋友

分类:

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.

条件判断

条件测试结构

  • 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
  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
阅读(2358) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~