从上节中(1. 常规的布尔运算),我们已经清楚地了解了 Shell 下的“逻辑值”是什么:是进程退出时的返回值,如果成功返回,则为真,如果不成功返回,则为假。
而条件测试正好使用了 test
这么一个指令,它用来进行数值测试(各种数值属性测试)、字符串测试(各种字符串属性测试)、文件测试(各种文件属性测试),我们通过判断对应的测试是否成功,从而完成各种常规工作,再加上各种测试的逻辑组合后,将可以完成更复杂的工作。
范例:数值测试
$ if test 5 -eq 5;then echo "YES"; else echo "NO"; fi
YES
$ if test 5 -ne 5;then echo "YES"; else echo "NO"; fi
NO
范例:字符串测试
$ if test -n "not empty";then echo "YES"; else echo "NO"; fi
YES
$ if test -z "not empty";then echo "YES"; else echo "NO"; fi
NO
$ if test -z "";then echo "YES"; else echo "NO"; fi
YES
$ if test -n "";then echo "YES"; else echo "NO"; fi
NO
范例:文件测试
$ if test -f /boot/System.map; then echo "YES"; else echo "NO"; fi
YES
$ if test -d /boot/System.map; then echo "YES"; else echo "NO"; fi
NO
各种逻辑测试的组合
范例:如果 a,b,c 都等于下面对应的值,那么打印 YES,通过 -a 进行"与"测试
$ a=5;b=4;c=6;
$ if test $a -eq 5 -a $b -eq 4 -a $c -eq 6; then echo "YES"; else echo "NO"; fi
YES
范例:测试某个“东西”是文件或者目录,通过 -o 进行“或”运算
$ if test -f /etc/profile -o -d /etc/profile;then echo "YES"; else echo "NO"; fi
YES
范例:测试某个“东西”是否为文件,测试 ! 非运算
$ if test ! -f /etc/profile; then echo "YES"; else echo "NO"; fi
NO
上面仅仅演示了 test
命令一些非常简单的测试,你可以通过 help test
获取 test
的更多用法。需要注意的是,test
命令内部的逻辑运算和 Shell 的逻辑运算符有一些区别,对应的为 -a
和 &&
,-o
与 ||
,这两者不能混淆使用。而非运算都是 !
,下面对它们进行比较。
比较 -a 与 &&, -o 与 ||, ! test 与 test
范例:要求某文件可执行且有内容,用 -a 和 && 分别实现
$ cat > test.sh
#!/bin/bash
echo "test"
[CTRL+D] # 按下组合键CTRL与D结束cat输入,后同,不再注明
$ chmod +x test.sh
$ if test -s test.sh -a -x test.sh; then echo "YES"; else echo "NO"; fi
YES
$ if test -s test.sh && test -x test.sh; then echo "YES"; else echo "NO"; fi
YES
范例:要求某个字符串要么为空,要么和某个字符串相等
$ str1="test"
$ str2="test"
$ if test -z "$str2" -o "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi
YES
$ if test -z "$str2" || test "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi
YES
范例:测试某个数字不满足指定的所有条件
$ i=5
$ if test ! $i -lt 5 -a $i -ne 6; then echo "YES"; else echo "NO"; fi
YES
$ if ! test $i -lt 5 -a $i -eq 6; then echo "YES"; else echo "NO"; fi
YES
很容易找出它们的区别,-a
和 -o
作为测试命令的参数用在测试命令的内部,而 &&
和 ||
则用来运算测试的返回值,!
为两者通用。需要关注的是:
- 有时可以不用
!
运算符,比如-eq
和-ne
刚好相反,可用于测试两个数值是否相等;-z
与-n
也是对应的,用来测试某个字符串是否为空 - 在
Bash
里,test
命令可以用[] 运算符取代,但是需要注意,[之后与
] 之前需要加上额外的空格 - 在测试字符串时,所有变量建议用双引号包含起来,以防止变量内容为空时出现仅有测试参数,没有测试内容的情况
下面我们用实例来演示上面三个注意事项:
-ne
和-eq
对应的,我们有时候可以免去!
运算
$ i=5
\( if test \)i -eq 5; then echo "YES"; else echo "NO"; fi
YES
\( if test \)i -ne 5; then echo "YES"; else echo "NO"; fi
NO
\( if test ! \)i -eq 5; then echo "YES"; else echo "NO"; fi
NO
- 用
[ ]
可以取代test
,这样看上去会“美观”很多
\( if [ \)i -eq 5 ]; then echo "YES"; else echo "NO"; fi
YES
$ if [ $i -gt 4 ] && [ $i -lt 6 ]; then echo "YES"; else echo "NO"; fi
YES
- 记得给一些字符串变量加上
""
,记得[
之后与]
之前多加一个空格
$ str=""
\( if [ "\)str" = "test"]; then echo "YES"; else echo "NO"; fi
-bash: [: missing `]'
NO
\( if [ \)str = "test" ]; then echo "YES"; else echo "NO"; fi
-bash: [: =: unary operator expected
NO
\( if [ "\)str" = "test" ]; then echo "YES"; else echo "NO"; fi
NO
到这里,条件测试就介绍完了,下面介绍命令列表,实际上在上面我们已经使用过了,即多个test命令的组合,通过 &&
,||
和 !
组合起来的命令序列。这种命令序列可以有效替换 if/then
的条件分支结构。这不难想到我们在 C 语言程序设计中经常做的如下的选择题(很无聊的例子,但是有意义):下面是否会打印 j
,如果打印,将打印什么?
#include <stdio.h>
int main()
{
int i, j;
i=5;j=1;
if ((i==5) && (j=5)) printf("%d\n", j);
return 0;
}
很容易知道将打印数字 5,因为 i==5
这个条件成立,而且随后是 &&
,要判断整个条件是否成立,我们得进行后面的判断,可是这个判断并非常规的判断,而是先把 j
修改为 5,再转换为真值,所以条件为真,打印出 5 。因此,这句可以解释为:如果 i
等于 5,那么把 j
赋值为 5,如果 j
大于 1 (因为之前已经为真),那么打印出 j
的值。这样用 &&
连结起来的判断语句替代了两个 if
条件分支语句。
正是基于逻辑运算特有的性质,我们可以通过 &&
,||
来取代 if/then
等条件分支结构,这样就产生了 命令列表。