$ if true;then echo YES; else echo NO; fiYES$ if false;then echo YES; else echo NO; fiNO
$ if true && true;then echo YES; else echo NO; fiYES$ if true && false;then echo YES; else echo NO; fiNO$ if false && false;then echo YES; else echo NO; fiNO$ if false && true;then echo YES; else echo NO; fiNO
$ if true || true;then echo YES; else echo NO; fiYES$ if true || false;then echo YES; else echo NO; fiYES$ if false || true;then echo YES; else echo NO; fiYES$ if false || false;then echo YES; else echo NO; fiNO
$ if ! false;then echo YES; else echo NO; fiYES$ if ! true;then echo YES; else echo NO; fiNO
可以看出
true和
false按照我们对逻辑运算的理解进行着,但是为了能够更好的理解 Shell 对逻辑运算的实现,我们还得弄清楚,
true和
false是怎么工作的?
回答是:否。
true和
false它们本身并非逻辑值,它们都是 Shell 的内置命令,只是它们的返回值是一个“逻辑值”:
$ true$ echo $?0$ false$ echo $?1
可以看到
true返回了 0,而
false则返回了 1 。跟我们离散数学里学的真值 1 和 0 并不是对应的,而且相反的。
$ help true falsetrue: true Return a successful result.false: false Return an unsuccessful result.$ type true falsetrue is a shell builtinfalse is a shell builtin
说明:
$?是一个特殊变量,存放有上一次进程的结束状态(退出状态码)。
从上面的操作不难联想到在 C 语言程序设计中为什么会强调在
main函数前面加上
int,并在末尾加上
return 0。因为在 Shell 里,将把 0 作为程序是否成功结束的标志,这就是 Shell 里头
true和
false的实质,它们用以反应某个程序是否正确结束,而并非传统的真假值(1 和 0),相反地,它们返回的是 0 和 1 。不过庆幸地是,我们在做逻辑运算时,无须关心这些。
从上节中,我们已经清楚地了解了 Shell 下的“逻辑值”是什么:是进程退出时的返回值,如果成功返回,则为真,如果不成功返回,则为假。
而条件测试正好使用了
test这么一个指令,它用来进行数值测试(各种数值属性测试)、字符串测试(各种字符串属性测试)、文件测试(各种文件属性测试),我们通过判断对应的测试是否成功,从而完成各种常规工作,再加上各种测试的逻辑组合后,将可以完成更复杂的工作。
$ if test 5 -eq 5;then echo YES; else echo NO; fiYES$ if test 5 -ne 5;then echo YES; else echo NO; fiNO
$ if test -n not empty;then echo YES; else echo NO; fiYES$ if test -z not empty;then echo YES; else echo NO; fiNO$ if test -z ;then echo YES; else echo NO; fiYES$ if test -n ;then echo YES; else echo NO; fiNO
$ if test -f /boot/System.map; then echo YES; else echo NO; fiYES$ if test -d /boot/System.map; then echo YES; else echo NO; fiNO
$ 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; fiYES
$ if test -f /etc/profile -o -d /etc/profile;then echo YES; else echo NO; fiYES
!非运算
$ if test ! -f /etc/profile; then echo YES; else echo NO; fiNO
上面仅仅演示了
test命令一些非常简单的测试,你可以通过
help test获取
test的更多用法。需要注意的是,
test命令内部的逻辑运算和 Shell 的逻辑运算符有一些区别,对应的为
-a和
&&,
-o与
||,这两者不能混淆使用。而非运算都是
!,下面对它们进行比较。
$ cat > test.sh#!/bin/bashecho 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; fiYES$ if test -s test.sh && test -x test.sh; then echo YES; else echo NO; fiYES
$ str1=test$ str2=test$ if test -z $str2 -o $str2 == $str1; then echo YES; else echo NO; fiYES$ if test -z $str2 || test $str2 == $str1; then echo YES; else echo NO; fiYES
$ i=5$ if test ! $i -lt 5 -a $i -ne 6; then echo YES; else echo NO; fiYES$ if ! test $i -lt 5 -a $i -eq 6; then echo YES; else echo NO; fiYES
很容易找出它们的区别,
-a和
-o作为测试命令的参数用在测试命令的内部,而
&&和
||则用来运算测试的返回值,
!为两者通用。需要关注的是:
有时可以不用
!运算符,比如
-eq和
-ne刚好相反,可用于测试两个数值是否相等;
-z与
-n也是对应的,用来测试某个字符串是否为空
在
Bash里,
test命令可以用[] 运算符取代,但是需要注意,[
之后与] 之前需要加上额外的空格
在测试字符串时,所有变量建议用双引号包含起来,以防止变量内容为空时出现仅有测试参数,没有测试内容的情况
下面我们用实例来演示上面三个注意事项:
-ne和
-eq对应的,我们有时候可以免去
!运算
$ i=5$ if test $i -eq 5; then echo YES; else echo NO; fiYES$ if test $i -ne 5; then echo YES; else echo NO; fiNO$ if test ! $i -eq 5; then echo YES; else echo NO; fiNO
用
[ ]可以取代
test,这样看上去会“美观”很多
$ if [ $i -eq 5 ]; then echo YES; else echo NO; fiYES$ if [ $i -gt 4 ] && [ $i -lt 6 ]; then echo YES; else echo NO; fiYES
记得给一些字符串变量加上
,记得[之后与
]之前多加一个空格
$ 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 expectedNO$ if [ $str = test ]; then echo YES; else echo NO; fiNO
到这里,条件测试就介绍完了,下面介绍命令列表,实际上在上面我们已经使用过了,即多个test命令的组合,通过
&&,
||和
!组合起来的命令序列。这种命令序列可以有效替换
if/then的条件分支结构。这不难想到我们在 C 语言程序设计中经常做的如下的选择题(很无聊的例子,但是有意义):下面是否会打印
j,如果打印,将打印什么?
#include <stdio.h>int main(){ int i, j; i=5;j=1; if ((i==5) && (j=5)) printf(%dn, j); return 0;}
很容易知道将打印数字 5,因为
i==5这个条件成立,而且随后是
&&,要判断整个条件是否成立,我们得进行后面的判断,可是这个判断并非常规的判断,而是先把
j修改为 5,再转换为真值,所以条件为真,打印出 5 。因此,这句可以解释为:如果
i等于 5,那么把
j赋值为 5,如果
j大于 1 (因为之前已经为真),那么打印出
j的值。这样用
&&连结起来的判断语句替代了两个
if条件分支语句。
正是基于逻辑运算特有的性质,我们可以通过
&&,
||来取代
if/then等条件分支结构,这样就产生了命令列表。
命令列表的执行规律符合逻辑运算的运算规律,用
&&连接起来的命令,如果前者成功返回,将执行后面的命令,反之不然;用
||连接起来的命令,如果前者成功返回,将不执行后续命令,反之不然。
$ ping -c 1 www.lzu.edu.cn -W 1 && echo =======connected=======
非常有趣的问题出来了,即我们上面已经提到的:为什么要让 C 程序在
main()函数的最后返回 0 ?如果不这样,把这种程序放入命令列表会有什么样的结果?你自己写个简单的 C 程序,然后放入命令列表看看。
有时用命令列表取代
if/then等条件分支结构可以省掉一些代码,而且使得程序比较美观、易读,例如:
#!/bin/bashecho $#echo $1if [ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null);then echo YESfi
说明:上例要求参数个数为 1 并且类型为数字。
再加上
exit 1,我们将省掉
if/then结构
#!/bin/bashecho $#echo $1! ([ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null)) && exit 1echo YES
这样处理后,对程序参数的判断仅仅需要简单的一行代码,而且变得更美观。
这一节介绍了 Shell 编程中的逻辑运算,条件测试和命令列表。