bash脚本中⽐较字符串的⽅法
您是否为 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 $?
[ian@pinguino ~]$ test -d "$HOME" ;echo $?
在清单 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 $?
[ian@pinguino ~]$ [ "abc" \< "def" ];echo $?
[ian@pinguino ~]$ [ "abc" \> "def" ];echo $?
1
[ian@pinguino ~]$ [ "abc" \<"abc" ];echo $?
1
[ian@pinguino ~]$ [ "abc" \> "abc" ];echo $?
1
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 $?
最后,-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
同使⽤ (( )) ⼀样,利⽤复合命令[[ ]] 可以对⽂件名和字符串使⽤更⾃然的语法。可以⽤括号和逻辑操
作符把 test 命令⽀持的测试组合起来。
清单 6. 使⽤[[复合命令
[ian@pinguino ~]$ [[ ( -d "$HOME" ) && ( -w "$HOME" ) ]] &&
>  echo "home is a writable directory"
home is a writable directory
在使⽤ = 或 != 操作符时,复合命令[[还能在字符串上进⾏模式匹配。匹配的⽅式就像清单 7 所⽰的通配符匹配。
清单 7. ⽤[[进⾏通配符测试
[ian@pinguino ~]$ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $?
[ian@pinguino ~]$ [[ "abc def c" == a[abc]*\ ?d* ]]; echo $?
1
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $?
1
甚⾄还可以在[[复合命令内执⾏算术测试,但是千万要⼩⼼。除⾮在 (( 复合命令内,否则 < 和 > 操作符会把操作数当成字符串⽐较并在当前
排序序列中测试它们的顺序。清单 8 ⽤⼀些⽰例演⽰了这⼀点。
清单 8. ⽤[[包含算术测试
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || (( 3 > 2 )) ]]; echo $?
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 -gt 2 ]]; echo $?
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 > 2 ]]; echo $?
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a > 2 ]]; echo $?
[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 值,但命令可能返回其他值。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。