原文发表于2017年第11期《网络安全与信息化》,转发到博客。

在Linux系统运维工作中,Shell脚本编程是运维人员应必须掌握的一项基本技能,在大多数的Shell脚本中都需要进行条件测试操作,从而使得脚本可以有选择地执行任务。本文将介绍如何通过逻辑运算符来同时测试多个条件,以及如何利用逻辑运算符来连接多条命令,从而实现简单高效的选择判断操作。

1. 逻辑运算符的基本用法

通过逻辑运算符可以同时测试多个条件,根据这些条件是否同时成立或者只要有其中一个条件成立等情况,来决定采取何种操作。逻辑运算符有:
 &&:逻辑与,表示前后两个条件都成立时整个测试结果才为真,否则结果为假。
 ||:逻辑或,表示前后两个条件至少有一个成立时整个测试结果即为真,否则结果为假。
 !:逻辑非,表示当指定的条件不成立时,整个测试结果为真。
逻辑运算符的基本用法非常简单,比如判断文件“/etc/passwd”和“/etc/shadow”是否存在,要求两个条件同时满足,则可以使用下面的表达式,并通过查看“$?”的值来获知测试结果,值为0表示条件成立,值为非0表示条件不成立。
[root@Server ~]# [ -e /etc/passwd ] && [ -e /etc/shadow ]
[root@Server ~]# echo $?
0
再比如要测试当前用户对文件“/etc/passwd”和“/etc/shadow”是否具有写入权限,只要满足其中一个条件即可,可以使用下面的表达式。
[root@Server ~]# [ -w /etc/passwd ] || [ -w /etc/shadow ]
[root@Server ~]# echo $?
0

2. 逻辑运算符连接多条命令

除了同时测试多个条件之外,逻辑运算符“&&”和“||”通常也用于间隔不同的命令操作,这也是本文要重点介绍的内容。比如表达式“command1 && command2”,在这个表达式中,如果command1的测试结果为假,那么command2就不会再执行。因为对于逻辑与操作,只要有一个测试条件不成立,那么整个测试结果也就必然为假,因而此时就无需再去判断command2是否成立。反之,如果command1的测试结果为真,则command2就必须执行。
如果使用逻辑运算符“||”来连接两个命令,情况又是不同。比如表达式“command1 || command2”,在这个表达式中,如果command1的测试结果为真,则command2不再执行。因为对于逻辑或操作,只要有一个测试条件成立,那么整个测试结果就必然为真,因而此时也无需再去判断command2是否为真。反之,如果command1的测试结果为假,则command2必须执行。
因而,对于“&&”和“||”的特点可以总结如下:
 &&,当前面的命令执行成功后才会执行后面的命令;
 ||,当前面的命令执行失败后才会执行后面的命令。

3. 实例演示

下面是几个具体的应用实例。
例:判断当前的用户是否是teacher,若不是则提示“Not teacher”。
[root@localhost ~]# echo $USER
root
[root@localhost ~]# [ $USER = "teacher" ] || echo "Not teacher"
Not teacher
例:判断当前的用户是否是teacher,若是则提示“Good Morning teacher”,若不是则提示“Not teacher”。
[root@localhost ~]# echo $USER
root
[root@localhost ~]# [ $USER = "teacher" ] && echo "Good morning teacher" || echo "not teacher"
not teacher
同样是上面这个操作,我们切换到teacher用户再来试一下。
[root@localhost ~]# useradd teacher
[root@localhost ~]# su - teacher
[teacher@localhost ~]$ [ $USER = "teacher" ] && echo "Good morning teacher" || echo "not teacher"
Good morning teacher
在这三个逻辑运算符中,“&&”尤为常用。比如要查看一个条件表达式的测试结果,如果通过查看预定义变量“$?”的值来进行判断,操作繁琐,输出结果也不是很直观。为了更便于查看条件测试操作的结果,通常都是将“&&”和echo命令一起使用,当条件成立时直接输出“yes”。
例:判断文件“/dev/cdrom”是否存在,如果存在,则直接输出yes。
[root@localhost ~]# [ -e /dev/cdrom ] && echo "yes"
yes
例:只要“/etc/rc.d/rc.local”或者“/etc/init.d/rc.local”中有一个是文件,则显示“YES”,否则无任何输出。
[root@localhost ~]# [ -f /etc/rc.d/rc.local ] || [ -f /etc/init.d/rc.local ] && echo "yes"
例:测试“/etc/profile”文件是否有可执行权限,若确实没有可执行权限,则提示“No x mode”的信息。
[root@localhost ~]# [ ! -x "etc/profile" ] && echo "No x mode"
No x mode
例:若当前用户是root且使用的shell程序是“/bin/bash”,则显示“yes”,否则无任何输出。
[root@localhost ~]# [ $USER = "root" ] && [ $SHELL = "/bin/bash" ] && echo "yes"
Yes
下面来编写一个具体的脚本,要实现的功能是删除“/root/test”目录中的test-1、test-2……test-10文件,但是test-3和test-7除外。
在脚本中用到了for循环和continue语句,continue语句在for循环中用于暂停本次操作,跳转至循环语句的顶部重新测试条件,本次执行过程中continue后的命令序列将被忽略。
[root@localhost ~]# ls /root/test
test-1 test-10 test-2 test-3 test-4 test-5 test-6 test-7 test-8 test-9
[root@localhost ~]# vim del.sh
#!/bin/bash
for i in $(seq 10)
do
#遇到需要保留的文件时跳过而不删除
[ $i -eq 3 ] || [ $i -eq 7 ] && continue
rm -f /root/test/test-$i
done
[root@localhost ~]# chmod u+x del.sh
[root@localhost ~]# ./del.sh
[root@localhost ~]# ls /root/test
test-3 test-7
因而,灵活运用 “&&”和“||”这两个逻辑运算符,在很多时候就可以代替复杂的if语句,从而使得我们的Shell脚本更加简捷高效。