您是否为 Bash shell
中大量的测试和比较选项而困惑呢?这个技巧可以帮助您解密不同类型的文件、算术和字符串测试,这样您就能够知道什么时候使用
test、 [ ]、 [[ ]]、
(( )) 或 if-then-else 了。
Bash shell 在当今的许多 Linux? 和 UNIX? 系统上都可使用,是 Linux 上常见的默认 shell。Bash 包含强大的编程功能,其中包括丰富的可测试文件类型和属性的函数,以及在多数编程语言中可以使用的算术和字符串比较函数。理解不同的测试并认识到 shell 还能把一些操作符解释成 shell 元字符,是成为高级 shell 用户的重要一步。这篇文章摘自 developerWorks 教程 LPI 102 考试准备,主题 109: Shell、脚本、编程和编译,介绍了如何理解和使用 Bash shell 的测试和比较操作。
这 个技巧解释了 shell 测试和比较函数,演示了如何向 shell 添加编程功能。您可能已经看到过使用 && 和 || 操作符的简单 shell 逻辑,它允许您根据前一条命令的退出状态(正确退出或伴随错误退出)而执行后一条命令。在这个技巧中,将看到如何把这些基本的技术扩展成更复杂的 shell 编程。
测试
在任何一种编程语言中,学习了如何给变量分配值和传递参数之后,都需要测试这些值和参数。在 shell 中,测试会设置返回的状态,这与其他命令执行的功能相同。实际上,test 是个内置命令!
test 和 [
内置命令 test 根据表达式expr 求值的结果返回 0(真)或 1(假)。也可以使用方括号:test expr和 [ expr ] 是等价的。 可以用 $? 检查返回值;可以使用 && 和 || 操作返回值;也可以用本技巧后面介绍的各种条件结构测试返回值。
清单 1. 一些简单测试
[ian@pinguino ~]$ test 3 -gt 4 && echo True || echo false false [ian@pinguino ~]$ [ "abc" != "def" ];echo $? 0 [ian@pinguino ~]$ test -d "$HOME" ;echo $? 0 |
在清单 1 的第一个示例中,-gt 操作符对两个字符值之间执行算术比较。在第二个示例中,用 [ ] 的形式比较两个字符串不相等。在最后一个示例中,测试 HOME 变量的值,用单目操作符 -d 检查它是不是目录。
可以用 -eq、 -ne、-lt、 -le、 -gt 或 -ge 比较算术值,它们分别表示等于、不等于、小于、小于等于、大于、大于等于。
可以分别用操作符 =、 !=、< 和 > 比较字符串是否相等、不相等或者第一个字符串的排序在第二个字符串的前面或后面。单目操作符 -z 测试 null 字符串,如果字符串非空 -n 返回 True(或者根本没有操作符)。
说明:shell 也用 < 和 > 操作符进行重定向,所以必须用 \< 或 \> 加以转义。清单 2 显示了字符串测试的更多示例。检查它们是否如您预期的一样。
清单 2. 一些字符串测试
[ian@pinguino ~]$ test "abc" = "def" ;echo $? 1 [ian@pinguino ~]$ [ "abc" != "def" ];echo $? 0 [ian@pinguino ~]$ [ "abc" \< "def" ];echo $? 0 [ian@pinguino ~]$ [ "abc" \> "def" ];echo $? 1 [ian@pinguino ~]$ [ "abc" \<"abc" ];echo $? 1 [ian@pinguino ~]$ [ "abc" \> "abc" ];echo $? 1 |
表 1 显示了一些更常见的文件测试。如果被测试的文件存在,而且有指定的特征,则结果为 True。
表 1. 一些常见的文件测试
操作符 | 特征 |
-d | 目录 |
-e | 存在(也可以用 -a) |
-f | 普通文件 |
-h | 符号连接(也可以用 -L) |
-p | 命名管道 |
-r | 可读 |
-s | 非空 |
-S | 套接字 |
-w | 可写 |
-N | 从上次读取之后已经做过修改 |
除了上面的单目测试,还可以使用表 2 所示的双目操作符比较两个文件:
表 2. 测试一对文件
操作符 | 为 True 的情况 |
-nt | 测试 file1 是否比 file2 更新。修改日期将用于这次和下次比较。 |
-ot | 测试 file1 是否比 file2 旧。 |
-ef | 测试 file1 是不是 file2 的硬链接。 |
其他一些测试可以用来测试文件许可之类的内容。请参阅 bash 手册获得更多细节或使用 help test 查看内置测试的简要信息。也可以用 help 命令了解其他内置命令。
-o 操作符允许测试利用 set -o 选项 设置的各种 shell 选项,如果设置了该选项,则返回 True (0),否则返回 False (1),如清单 3 所示。
清单 3. 测试 shell 选项
[ian@pinguino ~]$ set +o nounset [ian@pinguino ~]$ [ -o nounset ];echo $? 1 [ian@pinguino ~]$ set -u [ian@pinguino ~]$ test -o nounset; echo $? 0 |
最后,-a 和 -o 选项允许使用逻辑运算符 AND 和 OR 将表达式组合在一起。单目操作符 ! 可以使测试的意义相反。可以用括号把表达式分组,覆盖默认的优先级。请记住 shell 通常要在子 shell 中运行括号中的表达式,所以需要用 \( 和 \) 转义括号,或者把这些操作符括在单引号或双引号内。清单 4 演示了摩根法则在表达式上的应用。
清单 4. 组合和分组测试
[ian@pinguino ~]$ test "a" != "$HOME" -a 3 -ge 4 ; echo $? 1 [ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o 3 -lt 4 \) ]; echo $? 1 [ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o '(' 3 -lt 4 ')' ")" ]; echo $? 1 |
[[
test 命令非常强大,但是很难满足其转义需求以及字符串和算术比较之间的区别。幸运的是,bash 提供了其他两种测试方式,这两种方式对熟悉 C、C++ 或 Java? 语法的人来说会更自然些。
(( )) 复合命令 计算算术表达式,如果表达式求值为 0,则设置退出状态为 1;如果求值为非 0 值,则设置为 0。不需要对 (( 和 )) 之间的操作符转义。算术只对整数进行。除 0 会产生错误,但不会产生溢出。可以执行 C 语言中常见的算术、逻辑和位操作。 let 命令也能执行一个或多个算术表达式。它通常用来为算术变量分配值。
清单 5. 分配和测试算术表达式
[ian@pinguino ~]$ let x=2 y=2**3 z=y*3;echo $? $x $y $z 0 2 8 24 [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w 0 3 8 16 [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w 0 4 8 13 |
[[
清单 6. 使用 [[
[ian@pinguino ~]$ [[ ( -d "$HOME" ) && ( -w "$HOME" ) ]] && > echo "home is a writable directory" home is a writable directory |
[[
清单 7. 用 [[
[ian@pinguino ~]$ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def c" == a[abc]*\ ?d* ]]; echo $? 1 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $? 1 |
[[
清单 8. 用 [[
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || (( 3 > 2 )) ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 -gt 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 > 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a > 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a -gt 2 ]]; echo $? -bash: a: unbound variable |
条件测试
虽然使用以上的测试和 &&、 || 控制操作符能实现许多编程,但 bash 还包含了更熟悉的 “if, then, else” 和 case 结构。学习完这些之后,将学习循环结构,这样您的工具箱将真正得到扩展。
If、then、else 语句
bash 的 if 命令是个复合命令,它测试一个测试或命令($?)的返回值,并根据返回值为 True(0)或 False(不为 0)进行分支。虽然上面的测试只返回 0 或 1 值,但命令可能返回其他值。
http://www.server110.com/shell/201405/10643.html