当前位置: 首页 > 文档资料 > Shell 中文文档 >

第七章:测试

优质
小牛编辑
119浏览
2023-12-01

每一个完善的编程语言都应该能测试一个条件。然后依据测试的结果做进一步的动作。Bash有test命令,各种括号及内嵌的操作符,还有if/then结构来完成上面的功能。

一个if/then结构测试一列命令的退出状态是否为0(因为依照惯例,0意味着命令执行成功),如果是0则会执行一个或多个命令。

有一个命令[(左方括是特殊字符). 它和test是同义词,因为效率的原因,它被内建在shell里。这个命令的参数是比较表达式或者文件测试,它会返回一个退出状态指示比较的结果(0表示真,1表示假)。

在版本2.02,Bash引入了[[ ... ]]扩展的测试命令,它使熟悉其他语言中这种比较测试的程序员也能很快熟悉比较操作。注意[[是一个关键字,不是一个命令。

Bash把[[ $a -lt $b ]]看成一个返回退出状态的单元。

The (( ... ))and let ...constructs also return an exit status of 0 if the arithmetic expressions they evaluate expand to a non-zero value. These arithmetic expansion constructs may therefore be used to perform arithmetic comparisons.

   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"使grep禁止输出.
  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

谦虚的Stéphane 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 ]      # 0
  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 ]      # 1
  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            # 任意字符串为true.
  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            # 未初始化的变量为false.
  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            # 未始初化的变量为false.
  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都是关键字.关键字(或命令)开始一个语句,如果在同一行开始另一个新语句时,前面一个语句必须用分号(;)结束。

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

Else if 和 elif

elif

elif是else if的缩写。作用是在一个if/then里嵌入一个内部的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 ].如果用[ condition-true ]结构,左方括[, 是一个调用test命令的标识。右方括]在一个if/test中封闭左方括[,但它不是必须的,不过新一些的Bash版本会要求有。

Bash内建test命令测试文件类型和比较字符串. 因此,在一个Bash脚本中test语句不必调用外部的/usr/bin/test的二进制文件,这个test程序是sh-utils包的一部分。同样的,[也不调用/usr/bin/[,/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 #                               这个已经在bash 3.x版本被修补好了。
  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

[[]]结构比Bash版本的[]更通用。它是从ksh88中引进的test命令的扩展。

在[[和]]之间的所有的字符都不会被文件扩展或是标记分割,但是会有参数引用和命令替换。

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

[[ ... ]]测试结构比用[ ... ]更能防止脚本里的许多逻辑错误。比如说,&&,||,<和>操作符能在一个[[]]测试里通过,但在[]结构会发生错误。

在一个if的后面,不必一定是test命令或是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命令的退出状态码。

同样的,在一个测试方括号里面的条件测试也可以用列表结构(list construct)而不必用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或真作为退出状态码。这个结构和先前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 # 起了什么作用?
  34 # 如果不要"2>/dev/null"这句会怎么样?
  35 # 试试去掉这句再运行这个脚本.
  36
  37 exit 0