一、选择执行语句概述
面向过程的程序其流程控制结构主要有三种:1.顺序执行;2.循环执行;3.选择执行。前文中介绍了循环执行,本章及后续章节将重点介绍选择执行这种结构。
选择执行是指当程序执行过程中如果满足指定条件,就执行其中一部分内容,否则就执行其他内容,即只是有选择性的执行测试条件的相关内容。
现在设想,如果要添加用户user1,可以使用useradd user1来实现。但如果user1用户已经存在了,那么执行useradd user1时就会报错。为了避免这种情况,就需要在执行前先测试user1是否存在。其过程可以表示为:
如果条件满足:
显示“用户已存在,无需添加”;
如果条件不满足:
添加用户。
条件满足和条件不满足这二者只可能有一种情况,判断是否满足条件的过程,即为选择条件测试。
二、执行状态返回值
在bash中进行条件测试的方式通常有三种:
1.使用 [ expression ]
这里测试是否满足expression(表达式)后会得到一个返回值;注意,中括号和表达式之间必须有空格。
2.使用 [[ expression ]]
和第一种方式类似,不同之处在于一个是命令,一个是关键字
3.test expression
这里使用命令test来检测表达式
在实现条件测试时,可独立执行的命令,不需要使用上述测试方式;上述测试方式仅用于某些专用的测试环境。
如何确认测试条件的结果呢?在bash中,命令执行完成后,会产生一个命令执行状态返回值。命令执行状态返回值是和命令执行结果返回值相对应的概念,例如:
[root@localhost ~]# ls /varaccount cache crash cvs db empty games gdm lib local lock log mail nis opt preserve run
# 执行了 ls /var命令后,显示在屏幕上的 /var目录下的文件名,就是命令执行结果返回值。
# 但是怎么确定某个命令是否执行成功了呢?在bash中有一种特殊变量,是用来接收命令执行状态返回值的。
# 根据这个命令执行状态返回值,就可以确定该命令是否执行成功了。
Bash的变量类型主要有:本地变量、局部变量、环境变量、位置变量和特殊变量,其中特殊变量中的$?这个变量随时在变化,其中保存的数据就是刚刚执行过的命令状态返回值。
[root@localhost ~]# echo $?0
# 这里 $? 的值就是 ls /var这个命令的执行状态返回值
现在故意执行错误一次:
[root@localhost ~]# ls /varrls: cannot access /varr: No such file or directory
# /varr目录不存在,故报错; 报错的内容“ls: cannot access /varr: No such file or directory”
# 就是执行结果
[root@localhost ~]# echo $?2
# 可以看到 $?的值已经发生了改变,而这个值就是执行状态
[root@localhost ~]# lss /var-bash: lss: command not found
# lss 命令不存在
[root@localhost ~]# echo $?127
# $?的值又发生了改变
执行状态返回值$?的数字含义为:
0:成功执行;
1-255:执行失败。
其中1、2、127已经被系统占用了,如果用户想自行定义执行状态返回值,可以使用exit命令在脚本中定义脚本执行状态返回值,如果不定义,脚本执行状态返回值取决于脚本执行结束前最后执行的那条语句的状态。
故判断bash语句的执行条件,就是在判断执行状态返回值。
如果现在想判断当前主机上是否有root用户,可以进行如下操作:
[root@localhost ~]# id rootuid=0(root) gid=0(root) groups=0(root)
[root@localhost ~]# echo $?0
# root用户存在,故$?值为0
[root@localhost ~]# id roott &> /dev/null# 将id roott命令的结果输出到 /dev/null中
[root@localhost ~]# echo $?1
# $?值为1,说明不存在roott用户。
[root@localhost ~]# grep "^root:" /etc/passwd &> /dev/null
[root@localhost ~]# echo $?0
# 使用grep命令如果能查询到 /etc/passwd文件中以“root”开头的行,说明也存在root用户
[root@localhost ~]# grep "^roott:" /etc/passwd &> /dev/null
[root@localhost ~]# echo $?1
# grep命令查询不到“roott”,故$?的值为1
在bash编程中,测试某个条件是否满足,也是通过命令执行状态返回值来判断的。
再来看一个例子,在脚本中自行定义执行状态的返回值:
[root@localhosttutor]# vim if_user_exit.sh#!/bin/bash
#
if id $1 &> /dev/null;then
echo "Exists."
exit 0
#如果用户存在,定义执行返回值为0
else
echo "Not exist."
exit 8
#如果用户存在,定义执行返回值为8
fi
[root@localhosttutor]# bash -n if_user_exit.sh
[root@localhosttutor]# bash if_user_exit.sh user1Exists.
[root@localhosttutor]# echo $?0
[root@localhosttutor]# bash if_user_exit.sh suseNot exist.
[root@localhosttutor]# echo $?8
但是要注意的是,如果脚本中出现了exit,那么脚本会自动结束,其后的内容都不会执行了,故exit应该放在脚本需要结束的地方。如在刚才的脚本末尾添加一行:
[root@localhosttutor]# vim if_user_exit.sh#!/bin/bash
#
if id $1 &> /dev/null;then
echo "Exists."
exit 0
else
echo "Not exist."
exit 8
fi
echo "Hello Bash!"
[root@localhosttutor]# bash if_user_exit.sh suseNot exist.
#可以看到,脚本的最后一句echo "Hello Bash!"没有执行。
[root@localhosttutor]# echo $?8
如果既要定义exit的值,又要确保所有的应该执行的语句都能执行,可以对上述脚本进行优化:
[root@localhosttutor]# vim if_user_exit.sh#!/bin/bash
#
if id $1 &> /dev/null;then
echo "Exists."
Ret=0
#这里定义一个变量Ret用来存放返回值
else
echo "Not exist."
Ret=8
fi
echo "Hello Bash!"
exit $Ret
#执行状态返回值引用前面定义的变量Ret
[root@localhosttutor]# bash if_user_exit.sh suseNot exist.
Hello Bash!
#可以看到所有的语句都执行了
[root@localhosttutor]# echo $?8
#脚本的执行状态引用了变量Ret
三、单分支条件判断的模式
在shell脚本编程中,选择执行的方式主要有两种:
1. if 语句
2. case 语句
这里首先来介绍if语句的用法,其格式为:
if 条件; then
语句1
语句2
...
fi
或者写成:
if 条件
then
语句1
语句2
...
fi
这里使用if判断条件,如果满足,那么执行语句1、语句2...;如果不满足,则不执行。这种如果满足就执行,不满足就不执行的模式,称为单分支if语句。
四、单分支条件判断语句实例
下面来举例演示这种判断语句的用法:
例1:写一个脚本,如果用户存在,就显示该用户存在,否则不做任何操作:
[root@localhost ~]# vim /u01/tutor/if_user.sh
#!/bin/bash
UserName=user1
if id $UserName &> /dev/null; then
echo "$UserName exists."
fi
[root@localhost tutor]# chmod +x if_user.sh
[root@localhost tutor]# ./if_user.shuser1 exists.
[root@localhost tutor]# tail /etc/passwdsshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
user1:x:500:500::/home/user1:/bin/bash
# user1确实存在
[root@localhost tutor]# userdel -r user1# 删除 user1
[root@localhost tutor]# ./if_user.sh
[root@localhost tutor]# bash -x if_user.sh+ UserName=user1
+ id user1
# 查看脚本执行过程
[root@localhost tutor]# useradd user1# 添加用户user1
[root@localhost tutor]# bash -x if_user.sh# 显示运行过程
+ UserName=user1
+ id user1
+ echo 'user1 exists.'
user1 exists.
使用 grep命令也可以做类似判断:
[root@localhost tutor]# vim if_user2.sh#!/bin/bash
#
UserName=user1
if grep "^$UserName\>" /etc/passwd &> /dev/null; then
echo "$UserName exists."
fi
[root@localhost tutor]# bash -n if_user2.sh# 检测语法格式
[root@localhost tutor]# bash -x if_user2.sh+ UserName=user1
+ grep '^user1\>' /etc/passwd
+ echo 'user1 exists.'
user1 exists.
[root@localhost tutor]# userdel -r user1
[root@localhost tutor]# bash -x if_user2.sh+ UserName=user1
+ grep '^user1\>' /etc/passwd
fi 是if反过来写,表示if语句的结束,注意fi需要单独成为一行。
例2:写一个脚本,实现如下功能:如果用存在,就显示其UID和SHELL
这个例子和上一个很类似,只是把要显示的内容换成了uid和shell,可以采用截取的方法实现:
[root@localhost tutor]# vim if_uid.sh#!/bin/bash
#
UserName=user1
if id $UserName &> /dev/null; then
grep "^$UserName\>" /etc/passwd | cut -d: -f3,7
fi
[root@localhost tutor]# id user1id: user1: No such user
[root@localhost tutor]# useradd user1
[root@localhost tutor]# chmod +x if_uid.sh
[root@localhost tutor]# ./if_uid.sh500:/bin/bash
例3:写一个脚本,实现如下功能:如果设备/dev/sdb3已经挂载,就显示其挂载点
如果要查看某个设备是否挂载了,可以使用mount命令得到所有的挂载设备,然后使用grep命令查看具体某一个设备。
[root@localhost tutor]# mount/dev/mapper/VolGroup-lv_root on / type ext4 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
tmpfs on /dev/shm type tmpfs (rw,rootcontext="system_u:object_r:tmpfs_t:s0")
/dev/sda1 on /boot type ext4 (rw)
/dev/sdb3 on /mydata type ext4 (rw,noatime)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
gvfs-fuse-daemon on /root/.gvfs type fuse.gvfs-fuse-daemon (rw,nosuid,nodev)
/dev/sr0 on /media/20140715_2041 type iso9660 (ro,nosuid,nodev,uhelper=udisks,uid=0,gid=0,iocharset=utf8,mode=0400,dmode=0500)
[root@localhost tutor]# mount | grep "^/dev/sdb3"/dev/sdb3 on /mydata type ext4 (rw,noatime)
# 可以看到挂载信息,其中第三段 /mydata是挂载点
故此脚本可以写成如下形式:
[root@localhost tutor]# vim if_uid.sh#!/bin/bash
#
Device='/dev/sdb3'
if mount | grep "^$Device" &> /dev/null; then
mount | grep "$Device" | cut -d' ' -f3
fi
[root@localhost tutor]# bash -n mount_point.sh
[root@localhost tutor]# chmod +x mount_point.sh
[root@localhost tutor]# ./mount_point.sh
/mydata
例4:写一个脚本,实现如下功能:如果/etc/rc.d/rc.sysinit中有空白行,就显示其空白行数;
可以使用grep来查找空白行,然后再grep一次列出所有的空白行,最后使用wc命令来统计行数,具体操作如下:
[root@localhost tutor]# grep "^$" /etc/rc.d/rc.sysinit# 可以列出所有的空白行
[root@localhost tutor]# echo $?0
# 该语句执行成功,说明有空白行
[root@localhost tutor]# vim blank_line.sh#!/bin/bash
#
File='/etc/rc.d/rc.sysinit'
if grep "^$" $File &> /dev/null; then
grep "^$" $File |wc -l
fi
[root@localhost tutor]# bash -n blank_line.sh
[root@localhost tutor]# chmod +x blank_line.sh
[root@localhost tutor]# ./blank_line.sh
96