接着上一章继续
- 数值的对比
- 判断语句
- 循环语句
22.5 比较、对比、判断
在写脚本时,有时需要做一些比较,例如,两个数字谁大谁小,两个字符串是否相同等。
做对比的表达式有[]、[[]]、test,其中[]和 test这两种表达式的作用是相同的。[[]]和[]的不同
在于,[[]]能识别通配符和正则表达式中的元字符,[]却不能。
需要注意的是,在比较时,中括号和后续提及的比较符两边都要留有空格。
22.5.1数字的比较
数字的比较,主要是比较两个数字谁大谁小,或者是否相同。能用到的比较符有以下几
种。
(1)-eq:相等。
(2)-ne:不相等。
(3)-gt:大于。
(4)-ge:大于等于。
(5)-lt:小于。
(6)-le:小于等于。
做完比较之后,通过返回值来判断比较是否成立。
练习1:判断1等于2,命令如下。
[root@pp yy]# [ 1 -eq 2 ]
[root@pp yy]# echo $?
1
[root@pp yy]#
1是不能等于2的,所以判断不成立,返回值为非零。注意中括号和比较符两边的空格。
练习2:判断1不等于2,命令如下。
[root@pp yy]# [ 1 -ne 2 ]
[root@pp yy]# echo $?
0
[root@pp yy]#
1不等于2,所以判断成立,返回值为0
22.5.2 字待串的比较
字符串的比较,一般是比较两个字符串是否相同,用得较多的比较符有以下两种。
(1)==:相同。
(2)!=:不相同。
做完比较之后,通过返回值来判断比较是否成立。
练习1:定义一个变量aa=tom,然后做判断,命令如下。
[root@pp yy]# aa=tom
[root@pp yy]# [ $aa == tom ]
[root@pp yy]# echo $?
0
[root@pp yy]#
变量aa的值和 tom完全相同,所以判断成立,返回值为0。
练习2:在判断中匹配通配符,命令如下。
[root@pp yy]# aa=tom
[root@pp yy]# [ $aa == to? ]
[root@pp yy]# echo $?
1
[root@pp yy]#
这里定义aa=tom,按照前面讲过的通配符,to?匹配的应该是前两个字符为to,第三个
可以是任意字符,所以 tom应该会被to?匹配到,为什么返回值为非零呢?
原因在于在这一对中括号[]中是不能识别通配符的,aa的值是t、o、m三个字符,而等号
后面是t、o、?这三个字符,并没有把问号当成通配符,所以判断不成立。
如果想识别通配符,那么就要用双中括号[[]],看下面的判断。
[root@pp yy]# aa=tom
[root@pp yy]# [[[ $aa == to? ]]
[root@pp yy]# echo $?
0
[root@pp yy]#
在[[]]中能识别通配符"?",所以这里判断成立,返回值为0。
注意
(1)==后面跟的是通配符,如果想跟正则表达式,比较符就不能使用==了,要换成=~。
(2)一定要注意中括号和比较符两边的空格。
22.5.3 属性的判断
属性的判断,用于判断一个文件是否具备某个属性,常见的属性包括以下7种。
(1)-r:具备读权限。
(2)-w:具备写权限。
(3)-x:具备可执行权限。
注意
以上三个属性,不管是出现在u、g还是o上,只要有就算判断成立。
()-d:一个目录。
()-l:一个软链接。
()-f:一个普通文件,且要存在。
()-e:不管什么类型的文件,只要存在就算判断成立。
练习1:判断/etc/hosts具备r权限,命令如下。
[root@pp yy]# ls -l /etc/hosts
-rw-r--r--. 1 root root 158 9月 10 2018 /etc/hosts
[root@pp yy]# [ -r /etc/hosts ]
[root@pp yy]# echo $?
0
[root@pp yy]#
通过第一条命令可以看到/etc/hosts是具备r权限的,判断/etc/hosts具备r权限,自然成
立,所以返回值为0。
练习2:判断/etc/hosts具备x权限,命令如下。
[root@pp yy]# [ -x /etc/hosts ]
[root@pp yy]# echo $?
1
[root@pp yy]#
22.5.4 使用连接符
前面讲的判断只是单个判断,如果要同时做多个判断,那么就需要使用连接符了。能用的
连接符包括"&&"和"||"。
先看一下使用&&作为连接符,用法如下。
1 判断1 && 判断2
只有两个判断都为真(返回值为0),整体才为真,只要有一个为假,整体就为假。判断1
如果为假,判断2还有必要执行吗?没有,因为整体已经确定为假了。判断1为真,整体是真
是假在于判断2,所以判断2肯定是要执行的。
[root@pp yy]# [ 1 -le 2 ] && [ 2 -ge 3 ]
[root@pp yy]# echo $?
1
[root@pp yy]#
下面看使用||作为连接符,用法如下。
两个判断只要有一个为真(返回值为0),整体就为真,只有全都为假,整体才为假。
判断1为真,整体已经确定为真,所以判断2没有必要执行。
判断1为假,整体是真是假在于判断2,所以判断2肯定是要执行的。
[root@pp yy]# [ 1 -le 2 ] || [ 2 -ge 3 ]
[root@pp yy]# echo $?
0
[root@pp yy]#
这里有两个判断,第一个判断是1小于等于2,这个判断成立,整体已经确定为真,所以
整个判断为真,返回值为0。
22.6 if判断语句
在脚本中执行某条命令需要满足一定的条件,如果不满足就不能执行。此时我们就要用到
判断语句了。
先看if判断,if判断的语法如下。
1 if 条件1 ; then
2 命令1
3 elif 条件2 ; then
4 命令2
5 else 命令3
6 fi
先判断if后面的判断是不是成立。
如果成立,则执行命令1,然后跳到f后面,执行6后面的命令。
如果不成立,则不执行命令1,然后判断elif后面的条件2是不是成立。
如果成立,则执行命令2,然后跳到f后面,执行f后面的命令。
如果不成立,则不执行命令2,进行下一轮的elif 判断,以此类推。
如果所有if和elif都不成立,则执行clse中的命令3。
练习1:写一个脚本/opt/sc1.sh,要求只有root用户才能执行此脚本,其他用户不能执
行,命令如下。
[root@pp opt]# cat sc1.sh
#/bin/bash
if [ $UID ‐ne 0 ]; then
echo "只有root才能执行此脚本"
exit 1
fi
echo "hello root"
[root@pp opt]#
[root@pp opt]# chmod +x /opt/sc1.sh
脚本分析如下。
root的uid是0,其他用户的uid不为0。第一个判断,如果uid不等于0,则打印警告信
息"只有root才能执行此脚本",然后exit退出脚本。
如果这里不加 exit,判断之后仍然会继续执行echo "hello root"命令,这样判断就失去
了意义。只有加了exit之后,如果不是root,则到此结束,不要继续往下执行了。
如果是blab 执行此脚本,则判断成立,打印完警告信息之后,通过exit退出脚本。
如果是 root执行此脚本,则判断不成立,直接执行f后面的命令。
使用root用户执行此脚本的结果如下。
[root@pp opt]# ./sc1.sh
hello root
[root@pp opt]#
使用iu用户执行此脚本的结果如下。
[iu@pp opt]$ ./sc1.sh
只有root才能执行此脚本
[iu@pp opt]$
22.7 for循环语句
有时我们需要做多次重复的操作,例如,创建100个用户,创建一个用户需要两条命
令:useradd和 passwd。那么,创建100个用户就要重复执行100次,总共执行200条命令,
此时我们就可以利用for循环简化操作,让系统自动帮我们重复运行即可。
for循环的语法如下。
1 for 变量 in 值‐1 值‐2 值‐3 值‐4 ; do
2 命令 $变量
3 done
这里首先把值-1赋值给变量,执行do和done之间的命令,所有命令执行完成之后,再把
值-2赋值给变量,执行do和done之间的命令,执行完所有命令之后,再把值-3赋值给变
量,以此类推,直到把所有的值都赋值给变量。
看一个简单的例子,如下所示。
[iu@pp opt]$ for i in 1 2 3 4 ; do
> let i=$i+10
> echo $i
> done
11
12
13
14
[iu@pp opt]$
这里for后面定义了一个变量i,在in后面指定了4个值,分别是1、2、3、4。在do和done
之间定义了两个命令,第一个是在变量i的原有值的基础上加上10,然后打印i的值。
先把1赋值给i,此时i的值为1,执行do和 done之间的命令。i加上10之后,i的值变为了
11,然后打印i,得到11,第一次循环结束。
然后把2赋值给i,此时i的值为2,执行do和done之间的命令。i加上10之后,i的值变为了
12,然后打印i,得到12,第二次循环结束。
22.8 while 循环语句
while也可以循环,while循环的语法如下。
1 while 判断 ; do
2 命令1
3 命令2
4 done
如果while后面的判断成立,则执行do和 done之间的命令,在最后一个命令执行完成之
后,会回头再次判断一下while后面的判断是不是成立。如果不成立,则跳出循环执行done后
面的命令;如果成立,则继续执行do和 done之间的命令,就这样循环下去。
先看一个简单的例子,写一个脚本/opt/sc3.sh,命令如下。
[root@pp opt]# cat sc3.sh
#!/bin/bash
declare ‐i n=1
while [ $n -le 4 ] ; do
echo $n
let n=$n+1
done
[root@pp opt]#
[root@pp opt]# chmod +x /opt/sc3.sh
脚本分析如下。
这里先通过declare -i n=1定义了一个整数类型的变量n,初始值为1。然后进入 while进
行循环,先判断n的值是不是小于等于4,如果成立,则执行do和 done之间的命令。
一开始n的值为1,[ n -le 4 \]这个判断成立,则进人 do和done之间执行命令。首先打
印Sn的值,然后在此基础上给n 加上1,所以n的值变为了2,这样do和done之间的命令就 执行完成了。然后再次到while后面进行判断,此时n的值为2,依然满足小于等于4,再次
执行do 和 done之间的命令。
如此反复,当$n的值最终能增加到4时打印,然后加1,此时n的值变为了5。当Sn的值变
为5之后,while后面的判断就不再成立了,此时会跳出 while循环。
用while也可以用于循环一个文件的内容,用法如下。
1 while read aa ; do
2 命令
3 done < file
这里read后面的变量aa是可以随意指定的,整体的意思是首先读取file的第一行内容赋值
给aa,执行do和 done之间的命令。然后读取file的第二行内容赋值给aa,执行do和done
之间的命令,直到读取到file的最后一行。
有时while需要一直循环下去(死循环),语法如下。
1 while true ; do
2 命令
3 done
或
1 while ((1)) ; do
2 命令
3 done
或
1 while : ; do
2 命令
3 done
下面写一个脚本,来实时判断vsftpd是否启动,如果没有启动,则将vsftpd启动,命令如
下。
[root@pp opt]# cat sc4.sh
#!/bin/bash
while : ; do
systemctl is‐active vsftpd &> /dev/null
if [ $? -ne 0 ]; then
systemctl start vsftpd
fi
sleep 1
done
[root@pp opt]#
[root@pp opt]# chmod +x sc4.sh
这里写了一个 while循环,可以一直循环下去,循环中先判断vsftpd是否启动,如果启动
了则返回值为0,如果没有启动则返回值为非零。
下面开始根据返回值来进行判断,如果$?不等于0,说明vsftpd没有启动,则启动vsftpd
服务。sleep 1的意思是暂停1秒,这样就实现了每隔1秒来判断一次vsfilpd是否启动。
下面开始测试这个脚本,先把脚本放在后台运行,命令如下。
[root@pp opt]# ./sc4.sh &
[1] 3788
测试当前vsftpd 的状态,命令如下。
[root@pp opt]# systemctl is‐active vsftpd
active
[root@pp opt]#
关闭vsftpd服务之后,再次检测vsftpd 的状态,命令如下。
[root@pp opt]# systemctl stop vsftpd
[root@pp opt]# systemctl is‐active vsftpd
active
[root@pp opt]#
可以看到,vsftpd 仍然是启动的,说明我们的脚本生效了。