7.3 其他比较操作
二元比较操作可以比较变量或者数量。 需要注意的是,整数和字符串比较使用的是两套不同的操作符。
整数比较
-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" ] 和上面不等价。
==
等于
if [ "$a" == "$b" ]
和 = 同义
== 操作符在 双方括号 和单方括号里的功能是不同的。
1. [[ $a == z* ]] # $a 以 "z" 开头时为真(模式匹配)
2. [[ $a == "z*" ]] # $a 等于 z* 时为真(字符匹配)
4. [ $a == z* ] # 发生文件匹配和字符分割。
5. [ "$a" == "z*" ] # $a 等于 z* 时为真(字符匹配)
7. # 感谢 Stéphane Chazelas
!=
不等于
if [ "$a" != "$b" ]
在 [[ ... ]] 结构中会进行模式匹配。
<
小于,按照 ASCII码 排序。
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
注意在 [] 结构里 < 需要被 转义。
>
大于,按照 ASCII 码排序。
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
注意在 [] 结构里 > 需要被转义。
样例 27-11 包含了比较操作符。
-z
字符串为空,即字符串长度为0。
1. String='' # 长度为0的字符串变量。
3. if [ -z "$String" ]
4. then
5. echo "\$String is null."
6. else
7. echo "\$String is NOT null."
8. fi # $String is null.
-n
字符串非空(null)。
使用 -n 时字符串必须是在括号中且被引用的。使用 ! -z 判断未引用的字符串或者直接判断(样例 7-6)通常可行,但是非常危险。判断字符串时一定要引用[^1]。
样例 7-5. 算术比较和字符串比较
1. #!/bin/bash
3. a=4
4. b=5
6. # 这里的 "a" 和 "b" 可以是整数也可以是字符串。
7. # 因为 Bash 的变量是弱类型的,因此字符串和整数比较有很多相同之处。
9. # 在 Bash 中可以用处理整数的方式来处理全是数字的字符串。
10. # 但是谨慎使用。
12. echo
14. if [ "$a" -ne "$b" ]
15. then
16. echo "$a is not equal to $b"
17. echo "(arithmetic comparison)"
18. fi
20. echo
22. if [ "$a" != "$b" ]
23. then
24. echo "$a is not equal to $b."
25. echo "(string comparison)"
26. # "4" != "5"
27. # ASCII 52 != ASCIII 53
28. fi
30. # 在这个例子里 "-ne" 和 "!=" 都可以。
32. echo
34. exit 0
样例 7-6. 测试字符串是否为空(null)
1. #!/bin/bash
2. # str-test.sh: 测试是否为空字符串或是未引用的字符串。
4. # 使用 if [ ... ] 结构
6. # 如果字符串未被初始化,则其值是未定义的。
7. # 这种状态就是空 "null"(并不是 0)。
9. if [ -n $string1 ] # 并未声明或是初始化 string1。
10. then
11. echo "String \"string1\" is not null."
12. else
13. echo "String \"string1\" is null."
14. fi
15. # 尽管没有初始化 string1,但是结果显示其非空。
17. echo
19. # 再试一次。
21. if [ -n "$string1" ] # 这次引用了 $string1。
22. then
23. echo "String \"string1\" is not null."
24. else
25. echo "String \"string1\" is null."
26. fi # 在测试括号内引用字符串得到了正确的结果。
28. echo
30. if [ $string1 ] # 这次只有一个 $string1。
31. then
32. echo "String \"string1\" is not null."
33. else
34. echo "String \"string1\" is null."
35. fi # 结果正确。
36. # 独立的 [ ... ] 测试操作符可以用来检测字符串是否为空。
37. # 最好将字符串进行引用(if [ "$string1" ])。
38. #
39. # Stephane Chazelas 指出:
40. # if [ $string1 ] 只有一个参数 "]"
41. # if [ "$string1" ] 则有两个参数,空的 "$string1" 和 "]"
44. echo
47. string1=initialized
49. if [ $string1 ] # $string1 这次仍然没有被引用。
50. then
51. echo "String \"string1\" is not null."
52. else
53. echo "String \"string1\" is null."
54. fi # 这次的结果仍然是正确的。
55. # 最好将字符串引用("$string1")
58. string1="a = b"
60. if [ $string1 ] # $string1 这次仍然没有被引用。
61. then
62. echo "String \"string1\" is not null."
63. else
64. echo "String \"string1\" is null."
65. fi # 这次没有引用就错了。
67. exit 0 # 同时感谢 Florian Wisser 的提示。
样例 7-7. zmore
1. #!/bin/bash
2. # zmore
4. # 使用筛选器 'more' 查看 gzipped 文件。
6. E_NOARGS=85
7. E_NOTFOUND=86
8. E_NOTGZIP=87
10. if [ $# -eq 0 ] # 作用和 if [ -z "$1" ] 相同。
11. # $1 可以为空: zmore "" arg2 arg3
12. then
13. echo "Usage: `basename $0` filename" >&2
14. # 将错误信息通过标准错误 stderr 进行输出。
15. exit $E_NOARGS
16. # 脚本的退出状态为 85.
17. fi
19. filename=$1
21. if [ ! -f "$filename" ] # 引用字符串以防字符串中带有空格。
22. then
23. echo "File $filename not found!" >&2 # 通过标准错误 stderr 进行输出。
24. exit $E_NOTFOUND
25. fi
27. if [ ${filename##*.} != "gz" ]
28. # 在括号内使用变量代换。
29. then
30. echo "File $1 is not a gzipped file!"
31. exit $E_NOTGZIP
32. fi
34. zcat $1 | more
36. # 使用筛选器 'more'
37. # 也可以用 'less' 替代
39. exit $? # 脚本的退出状态由管道 pipe 的退出状态决定。
40. # 实际上 "exit $?" 不一定要写出来,
41. #+ 因为无论如何脚本都会返回最后执行命令的退出状态。
复合比较
-a
逻辑与
exp1 -a exp2 返回真当且仅当 exp1 和 exp2 均为真。
-o
逻辑或
如果 exp1 或 exp2 为真,则 exp1 -o exp2 返回真。
以上两个操作和 双方括号 结构中的 Bash 比较操作符号 && 和 || 类似。
1. [[ condition1 && condition2 ]]
测试操作 -o 和 -a 可以在 test 命令或在测试括号中进行。
1. if [ "$expr1" -a "$expr2" ]
2. then
3. echo "Both expr1 and expr2 are true."
4. else
5. echo "Either expr1 or expr2 is false."
6. fi
rihad 指出:
1. [ 1 -eq 1 ] && [ -n "`echo true 1>&2`" ] # 真
2. [ 1 -eq 2 ] && [ -n "`echo true 1>&2`" ] # 没有输出
3. # ^^^^^^^ 条件为假。到这里为止,一切都按预期执行。
5. # 但是
6. [ 1 -eq 2 -a -n "`echo true 1>&2`" ] # 真
7. # ^^^^^^^ 条件为假。但是为什么结果为真?
9. # 是因为括号内的两个条件子句都执行了么?
10. [[ 1 -eq 2 && -n "`echo true 1>&2`" ]] # 没有输出
11. # 并不是。
13. # 所以显然 && 和 || 具备“短路”机制,
14. #+ 例如对于 &&,若第一个表达式为假,则不执行第二个表达式直接返回假,
15. #+ 而 -a 和 -o 则不是。
复合比较操作的例子可以参考 样例 8-3,样例 27-17 和 样例 A-29。
[^1]: S.C. 指出在复合测试中,仅仅引用字符串可能还不够。比如表达式 [ -n "$string" -o "$a" = "$b" ] 在某些 Bash 版本下,如果 $string 为空可能会出错。更加安全的方式是,对于可能为空的字符串,添加一个额外的字符,例如 [ "x$string" != x -o "x$a" = "x$b" ](其中的 x 互相抵消)。
