目录
引言
如果想要使 Shell 脚本程序具备一定的智能化,那么就要知道如何区分不同的情况下该执行什么操作,下面我来总结一下如何进行条件测试操作,通过正确使用 if 语句,使 Shell 脚本程序具有一定的判断能力,能够根据不同的条件来完成不同的管理任务。
一、条件测试
1. test 命令
-
Shell 环境根据命令执行后的返回状态值 " $? " 来判断是否执行成功,当返回值为0时表示成功,否则表示失败或异常(非0值)。
-
使用专门的测试工具 test 命令,可以对特定条件进行测试,并根据返回值(值为0)来判断是否成立。
-
test 命令格式如下所示
test 条件表达式
或
[ 条件表达式 ] #括号与表达式之间需要至少一个空格进行分隔
或
[[ 条件表达式 ]]
两种方式作用完全相同,但通常后者形式更为常用,也更贴近编辑习惯。
2. 文件测试
-
文件测试指的是根据给定的路径名称,判断对应的是文件还是目录,或者判断文件是否可读、可写、可执行等。
-
常用操作选项如下,使用时将测试对象放在操作选项之后即可
选项 | 说明 |
---|---|
-d | 测试是否为目录(Directory) |
-e | 测试目录或文件是否存在(Exist) |
-f | 测试是否为文件(File) |
-r | 测试当前用户是否有权限读取(Read) |
-w | 测试当前用户是否有权限写入(Write) |
-x | 测试当前用户是否有权限执行(Excute) |
-b | 测试是否为测试文件 |
-c | 测试是否为字符设备文件 |
-s | 测试存在且文件大小为空 |
-L | 测试是否为链接文件 |
- 执行条件测试操作后,通过预定义变量 “$?” 可以获得测试命令的返回状态值,从而判断该条件是否成立
[root@localhost /home]#test -d /var/log/ && echo "是目录"
是目录 #输出"是目录"表示是目录
[root@localhost /home]#test -e /var/log/ && echo "存在"
存在
[root@localhost /home]#echo $? #查看命令的返回值
0 #返回0表示条件成立
[root@localhost /home]#[ -d /home/1.txt/ ]
[root@localhost /home]#echo $?
1 #返回1表示不成立
- 执行下面的操作可以判断 /opt/目录下是否存在名为 1.txt 的文件,如果不存在就创建一个 1.txt 的文件
[root@localhost /home]#[ ! -e /opt/1.txt ] && echo "yes" #“!”表示取反的意思
yes
[root@localhost /home]#ls /opt/
rh
[root@localhost /home]#[ ! -e /opt/1.txt ] && mkdir /opt/1.txt
[root@localhost /home]#ls /opt/
1.txt rh
3. 整数值比较
- 整数值比较是指根据给定的两个整数值来判断第一个数与第二个数的关系,如是否大于、等于、小于第二个数。
基本格式和常用选项如下:
[ 整数1 操作符 整数2 ]
格式 | 说明 |
---|---|
-eq | 第一个数等于(Equal)第二个数 |
-ne | 第一个数不等于(Not Equal)第二个数 |
-gt | 第一个数大于(Greater Than)第二个数 |
-lt | 第一个数小于( Lesser Than)第二个数 |
-le | 第一个数小于或等于(Lesser or Equal)第二个数 |
-ge | 第一个数大于或等于(Greater or Equal)第二个数 |
案例
① 执行下面的操作可以判断当前的用户数量,当超过3个时输出 "用户过多 !"
[root@localhost /home]#who | wc -l
4
[root@localhost /home]#[ $(who | wc -l) -gt 3 ] && echo "用户过多!"
用户过多!
② 若要判断当前可用的空闲内存大小,当低于1800MB时输出具体数值,可以执行下面的操作
[root@localhost /home]#FreeCC=$(free -m |grep "Mem:" | awk '{print $6}')
[root@localhost /home]#echo $?
0
[root@localhost /home]#free -m
total used free shared buff/cache available
Mem: 3774 735 1347 9 1692 2762
Swap: 4095 0 4095
[root@localhost /home]#[ $FreeCC -lt 1800 ] && echo ${FreeCC}MB
1692MB
4. 字符串比较
字符串比较通常用来检查用户输入、系统环境等是否满足条件,在提供交互式操作得 Shell 脚本中,也可以用来判断用户输入得位置参数是否符合要求。
- 常用的字符串比较操作选项如下
选项 | 说明 |
---|---|
= | 第一个字符串与第二个字符串相同 |
!= | 第一个字符串与第二个字符串不相同 |
-z | 检查字符串是否为空,对于未定义或赋予空值的变量将视为空串 |
案例
如果想要判断当前系统下的语言环境,当发现不是 “en.us” 时输出提示信息 “Not en.us” ,可以执行下面的操作
[root@localhost ~]#echo $LANG
zh_CN.UTF-8
[root@localhost ~]#[ $LANG != "en.us" ] && echo "Not en.us"
Not en.us
[root@localhost ~]#[ $LANG = "en.us" ] && echo "Not en.us" #没有输出结果,因为不成立
[root@localhost ~]#echo $?
1
[root@localhost ~]#read -p "是否覆盖该文件(yes/no)?" ACK
是否覆盖该文件(yes/no)?yes
[root@localhost ~]#[ $ACK = "yes" ] && echo "覆盖" #若输出的是yes则输出覆盖
覆盖
5. 逻辑测试
逻辑测试指的是判断两个或多个条件之间的依赖关系
- 基本格式如下
格式1 [ 表达式1 ] 操作符 [ 表达式2 ] ...
格式2 命令1 操作符 命令2 ...
- 常用的逻辑测试操作如下
① -a 或 &&:逻辑与,“而且”的意思
② -o 或 || :逻辑或,“或者”的意思
③ !:逻辑否
root@localhost ~]#[ -d /etc ] && [ -r /etc ] && echo "you can open it"
you can open it
[root@localhost ~]#echo $? #两个条件都满足,返回值为0
0
[root@localhost ~]#[ -f /etc ] && [ -r /etc ] && echo "you can open it"
[root@localhost ~]#echo $? #只满足一个条件,返回值为1
1
[root@localhost ~]#[ -d /etc ] || [ -r /home/test.txt ] && echo "ok"
ok #两个条件只要满足一个即可输出“ok”
[root@localhost ~]#ls /home/
1.txt 2.txt 3.txt add2num.sh gulei ip.txt user.sh XYXY.sh zhangsan
[root@localhost ~]#[ ! -f /etc && -r /etc ] && echo "ok"
-bash: [: 缺少 `]'
[root@localhost ~]#[ ! -f /etc -a -r /etc ] && echo "ok"
ok
#这里可以看到使用"&&"时报错,但是替换为"-a"后就可正常执行操作,所以说 Shell 脚本是可以灵活运用选项来操作的
[root@localhost ~]#uname -r #查看内核版本信息
3.10.0-693.el7.x86_64
[root@localhost ~]#Mnum=$(uname -r | awk -F. '{print $1}') #输出版本号,以"."进行分隔
[root@localhost ~]#echo $Mnum
3
[root@localhost ~]#Snum=$(uname -r | awk -F. '{print $2}') #输出版本号,以"."进行分隔
[root@localhost ~]#echo $Snum
10
[root@localhost ~]#[ $Mnum -ge 3 ] && [ $Snum -gt 6 ] && echo "true"
true #二个条件都满足,输出true
二、 if 语句
- 条件测试操作中,使用 “&&” 和 “||” 逻辑测试可以完成简单的判断并执行相应的操作,但是当我们需要执行的命令语句较多时,这种方式将使执行代码显得很复杂,不好理解。而使用专用的 if 条件语句,就可以更好地整理脚本结构,使得层次分明,清晰易懂。
1. 单分支 if 语句
-
if 语句的 “分支” 指的是不同测试结果所对应的执行语句(一条或多条)
-
对于单分支的选择结构,只有在 “条件成立” 时才会执行相应的代码,否则不执行任何操作
-
单分支 if 语句的语法格式如下所示:
if条件测试操作 #可以是[ 条件表达式 ]语句,也可以是其他可执行的命令语句
then
命令序列 #指的是一条或多条可执行的命令行,也包括嵌套使用的if语句或其他流程控制语句
fi
- 单分支if语句的执行流程如下
① 首先判断条件测试操作的结果,如果返回值为0,表示条件成立,则执行 then 后面的命令序列,一直到遇见 fi 结束判断为止,继续执行其他脚本代码
② 如果返回值不为0,则忽略 then 后面的命令序列,直接跳至 fi 行以后执行其他脚本代码
案例
[root@localhost /home]#vim test.sh
#!/bin/bash
if ls /opt > /dev/null
then
echo "it's ok"
fi
wq保存并退出
[root@localhost /home]#chmod +x test.sh
[root@localhost /home]#./test.sh
it's ok
[root@localhost /home]#ls /mnt/
[root@localhost /home]#vim test1.sh
#!/bin/bash
MOUNT_DIR="/mnt/centos7"
if [ ! -d $MOUNT_DIR ];then
mkdir -p $MOUNT_DIR
echo "$MOUNT_DIR 文件创建成功!"
fi
wq保存并退出
[root@localhost /home]#chmod +x test*
[root@localhost /home]#. test1.sh
/mnt/centos7 文件创建成功!
[root@localhost /home]#ls /mnt/
centos7
2. 双分支 if 语句
-
双分支 if 语句是在单分支的基础上针对 "条件不成立"的情况执行另一种操作,而不是不执行任何操作。
-
对于双分支的选择结构,要针对"条件成立" "条件不成立"两种情况分别执行不同的操作,语法格式如下
if条件测试操作
then
命令序列1
else
命令序列2
fi
- 双分支 if 语句的执行流程:
① 首先判断条件测试操作的结果,如果条件成立,则执行 then 后面的命令序列1,忽略 else 及后面的命今序列2,直到遇见 fi 结束判断
② 如果条件不成立,则忽略 then 及后面的命令序列1,直接跳至 else 后面的命令序列2并执行,直到遇见 fi 结束判断
案例
① 编写一个测试网络连通性的脚本,通过位置参数$IP 提供目标主机地址,然后根据 ping 检测结果给出相应的提示,操作如下
[root@localhost /home]#vim test2.sh
#!/bin/bash
IP=192.168.2.9
ping -c 2 -i 0.2 -W 3 $IP &>/dev/null #检查目标IP是否联通,多余的信息输入到/dev/null中
if [ $? -eq 0 ]
then
echo "$IP is up"
else
echo "$IP is down"
fi
[root@localhost /home]#chmod +x test2.sh
[root@localhost /home]#. test2.sh
192.168.2.9 is up
②
[root@localhost /home]#vim test3.sh
#!/bin/bash
if [ $UID -eq 0 ]
then
echo "当前用户为管理员root"
else
echo "当前用户为普通用户"
fi
[root@localhost /home]#bash ./test3.sh
当前用户为管理员root
3. 多分支 if 语句
- 与单分支、双分支 if 语句相比,多分支 if 语句的实际应用不多
- 能够根据多个互斥的条件分别执行不同的操作,实际上等同于嵌套使用的 if 语句
- 语法格式如下:
if 条件测试操作1
then 命令序列1
elif 条件测试操作2
then 命令序列2
else
命令序列3
fi
- 多分支 if 语句执行流程如下图所示:
案例
根据输入的考试成绩不同来区分优秀、及格、不及格三个档,操作如下
[root@localhost /home]#vim test4.sh
#!/bin/bash
read -p "请输入你的分数(0~100): " SCORE
if [ $SCORE -ge 85 ] && [ $SCORE -le 100 ] ;then
echo "$SCORE 分,优秀!"
elif [ $SCORE -ge 70 ] && [ $SCORE -le 84 ] ;then
echo ”$SCORE 分,及格!“
else
echo "$SCORE 分,不及格!"
fi
wq保存并退出
[root@localhost /home]#sh test4.sh
请输入你的分数(0~100): 99
99 分,优秀!
[root@localhost /home]#sh test4.sh
请输入你的分数(0~100): 75
”75 分,及格!“
[root@localhost /home]#sh test4.sh
请输入你的分数(0~100): 50
50 分,不及格!
三、case 语句
1. case 语句的结构
-
case 语句主要适用于某个变量存在多种取值,需要对其中的每一种取值分别执行不同的命令序列的情况(写服务脚本)
-
它和 if 语句十分相似,只不过 if 语句是需要判断多个不同的条件,而 case 语句只是判断一个变量的不同取值
-
case 分支语句的语法结构如下所示:
case 变量值 in
模式1)
命令序列 1
;;
模式2)
命令序列 2
;;
......
*) # ”*“代表任意
默认命令序列
esac
- case 语句的执行流程如下:
① 首先使用"变量值"与模式1进行比较,若取得相同则执行模式1后的命令序列,直到遇到双分号";;“后调至esac,表示结束分支
② 若与模式1不匹配,则继续与模式2进行比较,若取值相同则执行模式2后的命令序列,直到遇到双分号”;;“后调至esac,表示结束分支
③ 后面的以此类推…
④ 若找不到任何匹配的值,则执行默认模式”*)"后的命令序列,直到遇到esac后结束分支
- 使用case分支语句的时候,需要注意以下几点
① case 行尾必须为单词 “in”,每一个模式必须以右括号“)”结束
② 双分号 “;;”标识命令序列的结束
③ 模式字符串中,可以用方括号表示一个连续的范围,比如说“[0-9]”,也可以用竖杠“|”表示或,比如“a|b”
④ 最后的“*)”表示默认模式,星号相当于通配符
2. case 语句的应用
[root@localhost /home]#vim test5.sh
#!/bin/bash
case $1 in
start)
/usr/bin/systemctl $1 httpd
/usr/bin/ps aux |grep httpd
echo "httpd start"
;;
stop)
/usr/bin/systemctl $1 httpd
/usr/bin/ps aux |grep httpd
echo "httpd stop"
;;
restart)
echo "正在关闭 httpd 服务......"
/usr/bin/ps aux |grep httpd
/usr/bin/systemctl $1 httpd
echo "httpd 服务正在重新启动中......"
/usr/bin/ps aux |grep httpd
;;
status)
/usr/bin/systemctl $1 httpd
;;
*)
echo "please input start|stop|restart|status"
esac
:wq保存并退出
[root@localhost /home]#yum install -y httpd
[root@localhost /home]#bash test5.sh #除了设置的三个参数以外所有都会指向下面的输出结果
please input start|stop|restart|status
[root@localhost /home]#bash test5.sh start #启动httpd服务
root 12252 0.0 0.1 221944 4972 ? Ss 01:30 0:00 /usr/sbin/httpd -DFOREGROUND
apache 12259 0.0 0.0 224028 3092 ? S 01:30 0:00 /usr/sbin/httpd -DFOREGROUND
root 12260 0.0 0.0 112676 960 pts/0 S+ 01:30 0:00 grep httpd
apache 12261 0.0 0.0 224028 3092 ? S 01:30 0:00 /usr/sbin/httpd -DFOREGROUND
httpd start
[root@localhost /home]#netstat -antulp |grep httpd
tcp6 0 0 :::80 :::* LISTEN 12252/httpd
[root@localhost /home]#bash test5.sh stop #关闭httpd服务
root 12306 0.0 0.0 112676 960 pts/0 S+ 01:31 0:00 grep httpd
httpd stop
[root@localhost /home]#bash test5.sh restart #重启httpd服务
正在关闭 httpd 服务......
root 12344 0.0 0.1 221944 4968 ? Ss 01:32 0:00 /usr/sbin/httpd -DFOREGROUND
apache 12347 0.0 0.0 224028 3088 ? S 01:32 0:00 /usr/sbin/httpd -DFOREGROUND
apache 12351 0.0 0.0 224028 3088 ? S 01:32 0:00 /usr/sbin/httpd -DFOREGROUND
root 12367 0.0 0.0 112676 956 pts/0 S+ 01:34 0:00 grep httpd
httpd 服务正在重新启动中......
root 12381 0.0 0.1 221944 4972 ? Ss 01:34 0:00 /usr/sbin/httpd -DFOREGROUND
apache 12382 0.0 0.0 224028 3092 ? S 01:34 0:00 /usr/sbin/httpd -DFOREGROUND
apache 12386 0.0 0.0 224028 3092 ? S 01:34 0:00 /usr/sbin/httpd -DFOREGROUND
root 12388 0.0 0.0 112676 956 pts/0 S+ 01:34 0:00 grep httpd
[root@localhost /home]#bash test5.sh status #查看httpd服务状态
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Active: active (running) since 一 2021-09-06 01:34:15 CST; 4min 51s ago
Docs: man:httpd(8)
man:apachectl(8)
......
以下内容省略
总结
在Linux 系统中,/etc/rc.d/init.d 目录下绝大多数的系统服务脚本使用了 case 分支语句,平时控制各种系统服务时,提供的 start、stop、restart等位置参数,正是由 case 语句结构来识别并完成相应操作的!