1. 流程控制概述
1.1 流程控制简介
Shell 脚本默认从上到下顺序执行,在程序运行中,会遇到很多种情况,对应不同情况执行对应的操作,例如对于一批数据需要进行执行重复工作,这些都需要我们使用特定的流程控制语句来实现,我们想要程序完成预定的操作,就需要熟练掌握流程控制语句,不同的流程控制语句有不同的适应场景。
1.2 为什么要用流程控制
流程控制是每种编程语言控制逻辑走向和执行次序的重要组成部分,流程控制可以说是一门语言的 “经脉”,其控制着程序的运行走向,所以熟练掌握流程控制语句才能更好的控制整个脚本的运行结果,来完成我们的需求。
2. Shell 流程控制操作
2.1 条件语句
顾名思义,就是满足特定条件执行对应操作,按照顺序从上到下,条件语句 if 通常需要与 test 命令配合使用,当满足条件则执行 then
后的 command,否则继续往下运行执行对应的 command,条件语句 if 是 Shell 编程中最基础的条件判断流程语句。
2.1.1 单分支 if 语句
顾名思义就是只有一个 if 语句块包含的语句,condition 为正确则执行 then 内的命令,语法:
if conditionthen
command1
command2 ...
commandN
fi
对于 sshd 进程是否存在,可以使用单分支 if 语句来判断,例如:
if [ $(ps -ef |grep /usr/sbin/sshd|grep -v grep|wc -l) -eq 1 ];then
echo "sshd server exist"fi
2.1.2 双分支 if 语句
if conditionthen
command1
command2 ...
commandNelse
commandfi
统一判断 sshd 服务,可以在 else 中进行一些列操作,例如:
if [ $(ps -ef |grep /usr/sbin/sshd|grep -v grep|wc -l) -eq 1 ];then
echo "sshd server exist"else
service sshd start && echo "启动sshd服务"fi
2.1.3 多分支 if 语句
顾名思义有多个 if 条件,在此利用 elif 来表示,注意最后有一个 else 结尾。
if condition1;then
command1elif condition2;then
command2 ......elif conditionN,then
commandNelse
commandfi
我们的 linux 系统有多个版本,可以利用多分支 if 语句来进行判断,例如:
#! /bin/bashsys_version=$(rpm -q centos-release|cut -d- -f3)if [ $sys_version -eq 6 ];then
echo "sysversion is $sys_version"elif [ $sys_version -eq 7 ];then
echo "sysversion is $sys_version"else
echo "sysversion is ${sys_version}"fi
2.2 循环语句
对于一批数据,我们需要对其重复进行操作的时候,就需要利用循环语句来操作。
2.2.1 for 循环
for 循环语句通常应用在可预估数量的一批对象操作中,默认 for 循环的取值列表是以 $IFS
分割,默认 $IFS
为空白符,如果我们有其他需求可以更改,语法为:
for var in item1 item2 ... itemNdo
command1
command2 ...
commandNdone
通过 for 循环每次遍历一个后面跟的对象,在 do…done
操作块中对对象进行一些列操作。
SUM=0for num in $(seq 1 10)do
let SUM=${SUM}+${num}doneecho "1-10的和为:${SUM}"
当然在 for 循环语句里面也可以配合 if 条件判断或其他流程控制语句进行操作。
在此我们举例修改 $IFS
的应用场景,首选备份默认当前的 $IFS
,之后为其赋值新的 $IFS
为:
,在对 /etc/passwd 进行操作完成后,恢复之前的 $IFS
, 在此我们就利用改变 $IFS
对 /etc/passwd 的单个字段进行了变量操作。
#!/bin/bashOLD_IFS=$IFSIFS=":"for i in $(head -1 /etc/passwd); doecho $idoneIFS=${OLD_IFS}[root@xuel-terraform-cvm-0 ~]root
x
0
0
root
/root
/bin/bash[root@xuel-terraform-cvm-0 ~]root:x:0:0:root:/root:/bin/bash
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
for 循环如果条件永远满足则,一直执行内部的命令。
2.2.2 while 循环
while 循环同样为循环,与 for 循环功能一样,利用 for 循环的语句同样也可以使用 while 循环完成,但是 while 循环通常用于处理未知数量对象的操作,语法:
while 条件表达式:do commanddone
while 通常与 test 语句配合使用,如果条件表达式成立,则一直执行。
#!/bin/bashN=0while [ $N -lt 5 ]; do
let N++ echo $Ndone
也可以利用 read 读入文件,例如我们来读入一个写有 ip 或域名列表的文件,来判断该文件内的域名或 IP 网络是否可达。
#!/bin/bashfilename=urllist.txtfor url in $(cat $filename)do
status=`curl -I --connect-timeout 5 $url -s|awk '/HTTP/{print $2}'` if [[ $status == "200" ]];then
echo "Url:$url is ok! status is $status"
else
echo "Url:$url is error! status is $status"
fidone
[root@xuel-terraform-cvm-0 ~]baidu.com114.114.114.114[root@xuel-terraform-cvm-0 ~]Url:baidu.com is ok! status is 200Url:114.114.114.114 is error! status is
如果 while 的判断条件为永远为 true,则称为无限循环,会一直执行内部的操作,例如:
while :do
commanddone或者while truedo
commanddone
2.2.3 until 循环
until 循环与 while 循环刚好相反,其也有一定的应用场景,其为条件表达式为 true 时停止,否则一直运行,语法:
until 条件表达式do
commanddone
NUM=0until [ ${NUM} -ge 5 ]do
let NUM++ echo $NUMdone
2.2.4 break 与 continue
与上面三个循环语句不同的是,break 为跳出循环,continue 则为不执行下一次操作,直接跳到下一次循环。
#!/bin/bashN=0while true; do
let N++ if [ $N -eq 5 ]; then
break
fi
echo $Ndone
#!/bin/bashN=0while [ $N -lt 5 ]; do
let N++ if [ $N -eq 3 ]; then
continue
fi
echo $Ndone
2.3 选择语句
2.3.1 case 语句
选择语句 case 可以在特定的几个条件中选择某一个进行执行,其他 case 可以利用 if 多分支来替代。
case 模式名 in
模式 1)
命令 ;;
模式 2)
命令 ;;
*)
不符合以上模式执行的命令
esac
例如我们服务的启动操作脚本就是利用 case 语句来,当用户的输入与模式名相匹配则执行对应的命令。
#!/bin/bashcase $1 in
start)
echo "start."
;;
stop)
echo "stop."
;;
restart)
echo "restart."
;;
*)
echo "Usage: $0 {start|stop|restart}"esac
3. 实例
3.1 需求
编写一个脚本,来对当前系统 PATH
目录下的二进制执行文件进行,或者对指定全盘进行 md5 扫描,后期可以配合定时任务来监控文件是否变化,用于文件权限及内容的管控。
3.2 思路
文件扫描可以使用 md5sum 工具执行,对目录遍历需要使用 for 循环,现在执行方式可以使用 case 来操作,其中一些操作会涉及到 sed 的命令,后期我们会针对这些命令进行单独章节详细讲解,在本需求案例中着重思考 Shell 中流程控制的作用。
3.3 实现
#!/bin/bashSCAN_DIR=`echo $PATH |sed 's/:/ /g'`SCAN_CMD=`which md5sum`
SCAN_FILE_FAIL="/tmp/scan_$(date +%F%H%m)_fall.txt"SCAN_FILE_BIN="/tmp/scan_$(date +%F%H%m)_bin.txt"scan_fall_disk() {
echo "正在全盘扫描,请稍等!文件路径:$SCAN_FILE_FALL"
find / -type f ! -path "/proc/*" -exec $SCAN_CMD \{\} \;>> $SCAN_FILE_FAIL 2>/dev/null echo "扫描完成,可利用以下命令后期对文件进行校验"
echo "$SCAN_CMD -c $SCAN_FILE_FAIL |grep -v 'OK$'"}scan_bin() {
echo "正在扫描$PATH可执行文件,请稍等,文件路径:$SCAN_FILE_BIN"
for file in $SCAN_DIR
do
find $file -type f -exec $SCAN_CMD \{\} \;>> $SCAN_FILE_BIN 2>/dev/null done
echo "扫描完成,可利用以下命令后期对文件进行校验"
echo "$SCAN_CMD -c $SCAN_FILE_BIN |grep -v 'OK$'"}clearecho "##########################################"echo "# #"echo "# 利用md5sum对文件进行校验 #"echo "# #"echo "##########################################"echo "1: 全盘扫描"echo "2: bin path扫描"echo "3: EXIT"read -p "Please input your choice:" methodcase $method in 1)
scan_fall_disk;;2)
scan_bin;;3)
echo "you choce channel!" && exit 1;;*)
echo "input Error! Place input{1|2|3}" && exit 0;;esac
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
[root@xuel-terraform-cvm-0 ~]1: 全盘扫描2: bin path扫描3: EXITPlease input your choice:2正在扫描/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin可执行文件,请稍等,文件路径:/tmp/scan_2020-03-271703_bin.txt
扫描完成,可利用以下命令后期对文件进行校验/usr/bin/md5sum -c /tmp/scan_2020-03-271703_bin.txt |grep -v 'OK$'[root@xuel-terraform-cvm-0 ~]/sbin/mii-tool: 确定/sbin/wipefs: 确定/sbin/blkdiscard: 确定/sbin/rtmon: 确定/sbin/shutdown: 确定/sbin/aureport: 确定/sbin/plymouthd: 确定/sbin/udevd: 确定/sbin/lvmetad: 确定/sbin/e2undo: 确定...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
当我们输入数字 2 的时候即对 bin 路径执行扫描,扫描完成后会生成对应的扫描文件,可以执行 /usr/bin/md5sum -c /tmp/scan_2020-03-271703_bin.txt |grep -v 'OK$'
来进行后期文件校验。
[root@xuel-terraform-cvm-0 ~]1: 全盘扫描2: bin path扫描3: EXITPlease input your choice:3you choce channel!
当输入 3 程序退出,在此就是使用 case 来完成。
[root@xuel-terraform-cvm-0 ~]1: 全盘扫描2: bin path扫描3: EXITPlease input your choice:1正在全盘扫描,请稍等!文件路径:
扫描完成,可利用以下命令后期对文件进行校验/usr/bin/md5sum -c /tmp/scan_2020-03-271703_fall.txt |grep -v 'OK$'[root@xuel-terraform-cvm-0 ~]/sys/devices/platform/uevent: 确定/sys/devices/platform/power/control: 确定/sys/devices/platform/power/wakeup: 确定/sys/devices/platform/pcspkr/uevent: 确定/sys/devices/platform/pcspkr/modalias: 确定/sys/devices/platform/pcspkr/power/control: 确定/sys/devices/platform/pcspkr/power/wakeup: 确定/sys/devices/platform/platform-framebuffer.0/uevent: 确定/sys/devices/platform/platform-framebuffer.0/modalias: 确定/sys/devices/platform/platform-framebuffer.0/power/control: 确定/sys/devices/platform/platform-framebuffer.0/power/wakeup: 确定/sys/devices/platform/serial8250/uevent: 确定/sys/devices/platform/serial8250/modalias: 确定/sys/devices/platform/serial8250/power/control: 确定/sys/devices/platform/serial8250/power/wakeup: 确定/sys/devices/platform/serial8250/tty/ttyS1/uevent: 确定/sys/devices/platform/serial8250/tty/ttyS1/dev: 确定/sys/devices/platform/serial8250/tty/ttyS1/power/cont
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
4. 注意事项
- 对于条件语句中的多分支 if 语句,结尾肯定是有一个 else 来完成,需要注意此点;
- 在编写流程控制时候,可以根据自己的习惯对于
command
中的操作进行缩进,可以为几个空格,也可以为一个 tab 键,建议使用一个制表符; - 流程控制语句可以相互嵌套,例如 for 循环中可以有 if 条件判断,if 添加判断中也可以添加 for 循环等,根据需求具体灵活运用;
- case 语句与 if 多分支语句一样,不同点在与 case 语句只能判断一种条件关系,而 if 多分支可以对多种条件进行判断。
5. 小结
流程控制语句是我们 Shell 编写的经络,利用它来控制程序的走向,需要我们能熟练掌握最常用的流程控制语句,并知道其对应的适应场景,灵活配合使用。