Shell 流程控制
1. 流程控制概述
1.1 流程控制簡介
Shell 腳本默認(rèn)從上到下順序執(zhí)行,在程序運(yùn)行中,會(huì)遇到很多種情況,對(duì)應(yīng)不同情況執(zhí)行對(duì)應(yīng)的操作,例如對(duì)于一批數(shù)據(jù)需要進(jìn)行執(zhí)行重復(fù)工作,這些都需要我們使用特定的流程控制語句來實(shí)現(xiàn),我們想要程序完成預(yù)定的操作,就需要熟練掌握流程控制語句,不同的流程控制語句有不同的適應(yīng)場景。
1.2 為什么要用流程控制
流程控制是每種編程語言控制邏輯走向和執(zhí)行次序的重要組成部分,流程控制可以說是一門語言的 “經(jīng)脈”,其控制著程序的運(yùn)行走向,所以熟練掌握流程控制語句才能更好的控制整個(gè)腳本的運(yùn)行結(jié)果,來完成我們的需求。
2. Shell 流程控制操作
2.1 條件語句
顧名思義,就是滿足特定條件執(zhí)行對(duì)應(yīng)操作,按照順序從上到下,條件語句 if 通常需要與 test 命令配合使用,當(dāng)滿足條件則執(zhí)行 then
后的 command,否則繼續(xù)往下運(yùn)行執(zhí)行對(duì)應(yīng)的 command,條件語句 if 是 Shell 編程中最基礎(chǔ)的條件判斷流程語句。
2.1.1 單分支 if 語句
顧名思義就是只有一個(gè) if 語句塊包含的語句,condition 為正確則執(zhí)行 then 內(nèi)的命令,語法:
if condition
then
command1
command2
...
commandN
fi
對(duì)于 sshd 進(jìn)程是否存在,可以使用單分支 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 語句存在 else 情況,語法:
if condition
then
command1
command2
...
commandN
else
command
fi
統(tǒng)一判斷 sshd 服務(wù),可以在 else 中進(jìn)行一些列操作,例如:
if [ $(ps -ef |grep /usr/sbin/sshd|grep -v grep|wc -l) -eq 1 ];then
echo "sshd server exist"
else
service sshd start && echo "啟動(dòng)sshd服務(wù)"
fi
2.1.3 多分支 if 語句
顧名思義有多個(gè) if 條件,在此利用 elif 來表示,注意最后有一個(gè) else 結(jié)尾。
if condition1;then
command1
elif condition2;then
command2
...
...
elif conditionN,then
commandN
else
command
fi
我們的 linux 系統(tǒng)有多個(gè)版本,可以利用多分支 if 語句來進(jìn)行判斷,例如:
#! /bin/bash
sys_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 循環(huán)語句
對(duì)于一批數(shù)據(jù),我們需要對(duì)其重復(fù)進(jìn)行操作的時(shí)候,就需要利用循環(huán)語句來操作。
2.2.1 for 循環(huán)
for 循環(huán)語句通常應(yīng)用在可預(yù)估數(shù)量的一批對(duì)象操作中,默認(rèn) for 循環(huán)的取值列表是以 $IFS
分割,默認(rèn) $IFS
為空白符,如果我們有其他需求可以更改,語法為:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
通過 for 循環(huán)每次遍歷一個(gè)后面跟的對(duì)象,在 do…done
操作塊中對(duì)對(duì)象進(jìn)行一些列操作。
例如我們來求和 1-10 的和:
SUM=0
for num in $(seq 1 10)
do
let SUM=${SUM}+${num}
done
echo "1-10的和為:${SUM}"
當(dāng)然在 for 循環(huán)語句里面也可以配合 if 條件判斷或其他流程控制語句進(jìn)行操作。
在此我們舉例修改 $IFS
的應(yīng)用場景,首選備份默認(rèn)當(dāng)前的 $IFS
,之后為其賦值新的 $IFS
為:
,在對(duì) /etc/passwd 進(jìn)行操作完成后,恢復(fù)之前的 $IFS
, 在此我們就利用改變 $IFS
對(duì) /etc/passwd 的單個(gè)字段進(jìn)行了變量操作。
#!/bin/bash
OLD_IFS=$IFS
IFS=":"
for i in $(head -1 /etc/passwd); do
echo $i
done
IFS=${OLD_IFS}
[root@xuel-terraform-cvm-0 ~]# bash 1.sh
root
x
0
0
root
/root
/bin/bash
[root@xuel-terraform-cvm-0 ~]# cat /etc/passwd |head -1
root:x:0:0:root:/root:/bin/bash
for 循環(huán)如果條件永遠(yuǎn)滿足則,一直執(zhí)行內(nèi)部的命令。
for (( ; ; ))
2.2.2 while 循環(huán)
while 循環(huán)同樣為循環(huán),與 for 循環(huán)功能一樣,利用 for 循環(huán)的語句同樣也可以使用 while 循環(huán)完成,但是 while 循環(huán)通常用于處理未知數(shù)量對(duì)象的操作,語法:
while 條件表達(dá)式:do
command
done
while 通常與 test 語句配合使用,如果條件表達(dá)式成立,則一直執(zhí)行。
例如求和打印 1-5 個(gè)數(shù):
#!/bin/bash
N=0
while [ $N -lt 5 ]; do
let N++
echo $N
done
也可以利用 read 讀入文件,例如我們來讀入一個(gè)寫有 ip 或域名列表的文件,來判斷該文件內(nèi)的域名或 IP 網(wǎng)絡(luò)是否可達(dá)。
#!/bin/bash
#function:check url
filename=urllist.txt
for 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"
fi
done
編寫 urllist.txt
。
[root@xuel-terraform-cvm-0 ~]# cat urllist.txt
baidu.com
114.114.114.114
[root@xuel-terraform-cvm-0 ~]# bash urlcheck.sh
Url:baidu.com is ok! status is 200
Url:114.114.114.114 is error! status is
如果 while 的判斷條件為永遠(yuǎn)為 true,則稱為無限循環(huán),會(huì)一直執(zhí)行內(nèi)部的操作,例如:
while :
do
command
done
或者
while true
do
command
done
2.2.3 until 循環(huán)
until 循環(huán)與 while 循環(huán)剛好相反,其也有一定的應(yīng)用場景,其為條件表達(dá)式為 true 時(shí)停止,否則一直運(yùn)行,語法:
until 條件表達(dá)式
do
command
done
例如我們使用 until 來打印 1-5 數(shù)字:
NUM=0
until [ ${NUM} -ge 5 ]
do
let NUM++
echo $NUM
done
2.2.4 break 與 continue
與上面三個(gè)循環(huán)語句不同的是,break 為跳出循環(huán),continue 則為不執(zhí)行下一次操作,直接跳到下一次循環(huán)。
我們可以利用 break 來跳出終止循環(huán)。
- break
#!/bin/bash
N=0
while true; do
let N++
if [ $N -eq 5 ]; then
break
fi
echo $N
done
- continue
#!/bin/bash
N=0
while [ $N -lt 5 ]; do
let N++
if [ $N -eq 3 ]; then
continue
fi
echo $N
done
利用 continue 來跳過特定的條件操作。
2.3 選擇語句
2.3.1 case 語句
選擇語句 case 可以在特定的幾個(gè)條件中選擇某一個(gè)進(jìn)行執(zhí)行,其他 case 可以利用 if 多分支來替代。
case 模式名 in
模式 1)
命令
;;
模式 2)
命令
;;
*)
不符合以上模式執(zhí)行的命令
esac
例如我們服務(wù)的啟動(dòng)操作腳本就是利用 case 語句來,當(dāng)用戶的輸入與模式名相匹配則執(zhí)行對(duì)應(yīng)的命令。
#!/bin/bash
case $1 in
start)
echo "start."
;;
stop)
echo "stop."
;;
restart)
echo "restart."
;;
*)
echo "Usage: $0 {start|stop|restart}"
esac
3. 實(shí)例
3.1 需求
編寫一個(gè)腳本,來對(duì)當(dāng)前系統(tǒng) PATH
目錄下的二進(jìn)制執(zhí)行文件進(jìn)行,或者對(duì)指定全盤進(jìn)行 md5 掃描,后期可以配合定時(shí)任務(wù)來監(jiān)控文件是否變化,用于文件權(quán)限及內(nèi)容的管控。
3.2 思路
文件掃描可以使用 md5sum 工具執(zhí)行,對(duì)目錄遍歷需要使用 for 循環(huán),現(xiàn)在執(zhí)行方式可以使用 case 來操作,其中一些操作會(huì)涉及到 sed 的命令,后期我們會(huì)針對(duì)這些命令進(jìn)行單獨(dú)章節(jié)詳細(xì)講解,在本需求案例中著重思考 Shell 中流程控制的作用。
3.3 實(shí)現(xiàn)
- 具體實(shí)現(xiàn) shell 腳本
#!/bin/bash
# Description: count file scripts
# Auth: kaliarch
# Email: kaliarch@163.com
# function: count file
# Date: 2020-03-28 14:00
# Version: 1.0
# 定義文件掃描目錄
SCAN_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 "正在全盤掃描,請(qǐng)稍等!文件路徑:$SCAN_FILE_FALL"
find / -type f ! -path "/proc/*" -exec $SCAN_CMD \{\} \;>> $SCAN_FILE_FAIL 2>/dev/null
echo "掃描完成,可利用以下命令后期對(duì)文件進(jìn)行校驗(yàn)"
echo "$SCAN_CMD -c $SCAN_FILE_FAIL |grep -v 'OK$'"
}
scan_bin() {
echo "正在掃描$PATH可執(zhí)行文件,請(qǐng)稍等,文件路徑:$SCAN_FILE_BIN"
for file in $SCAN_DIR
do
find $file -type f -exec $SCAN_CMD \{\} \;>> $SCAN_FILE_BIN 2>/dev/null
done
echo "掃描完成,可利用以下命令后期對(duì)文件進(jìn)行校驗(yàn)"
echo "$SCAN_CMD -c $SCAN_FILE_BIN |grep -v 'OK$'"
}
clear
echo "##########################################"
echo "# #"
echo "# 利用md5sum對(duì)文件進(jìn)行校驗(yàn) #"
echo "# #"
echo "##########################################"
echo "1: 全盤掃描"
echo "2: bin path掃描"
echo "3: EXIT"
# 選擇掃描方式
read -p "Please input your choice:" method
case $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
- 測試
[root@xuel-terraform-cvm-0 ~]# bash file_scan.sh
##########################################
# #
# 利用md5sum對(duì)文件進(jìn)行校驗(yàn) #
# #
##########################################
1: 全盤掃描
2: bin path掃描
3: EXIT
Please input your choice:2
正在掃描/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin可執(zhí)行文件,請(qǐng)稍等,文件路徑:/tmp/scan_2020-03-271703_bin.txt
掃描完成,可利用以下命令后期對(duì)文件進(jìn)行校驗(yàn)
/usr/bin/md5sum -c /tmp/scan_2020-03-271703_bin.txt |grep -v 'OK$'
[root@xuel-terraform-cvm-0 ~]# /usr/bin/md5sum -c /tmp/scan_2020-03-271703_bin.txt |grep -v 'OK$'
/sbin/mii-tool: 確定
/sbin/wipefs: 確定
/sbin/blkdiscard: 確定
/sbin/rtmon: 確定
/sbin/shutdown: 確定
/sbin/aureport: 確定
/sbin/plymouthd: 確定
/sbin/udevd: 確定
/sbin/lvmetad: 確定
/sbin/e2undo: 確定
...
當(dāng)我們輸入數(shù)字 2 的時(shí)候即對(duì) bin 路徑執(zhí)行掃描,掃描完成后會(huì)生成對(duì)應(yīng)的掃描文件,可以執(zhí)行 /usr/bin/md5sum -c /tmp/scan_2020-03-271703_bin.txt |grep -v 'OK$'
來進(jìn)行后期文件校驗(yàn)。
[root@xuel-terraform-cvm-0 ~]# bash file_scan.sh
##########################################
# #
# 利用md5sum對(duì)文件進(jìn)行校驗(yàn) #
# #
##########################################
1: 全盤掃描
2: bin path掃描
3: EXIT
Please input your choice:3
you choce channel!
當(dāng)輸入 3 程序退出,在此就是使用 case 來完成。
[root@xuel-terraform-cvm-0 ~]# bash file_scan.sh
##########################################
# #
# 利用md5sum對(duì)文件進(jìn)行校驗(yàn) #
# #
##########################################
1: 全盤掃描
2: bin path掃描
3: EXIT
Please input your choice:1
正在全盤掃描,請(qǐng)稍等!文件路徑:
掃描完成,可利用以下命令后期對(duì)文件進(jìn)行校驗(yàn)
/usr/bin/md5sum -c /tmp/scan_2020-03-271703_fall.txt |grep -v 'OK$'
[root@xuel-terraform-cvm-0 ~]# /usr/bin/md5sum -c /tmp/scan_2020-03-271703_fall.txt |grep -v 'OK$' |more
/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
4. 注意事項(xiàng)
- 對(duì)于條件語句中的多分支 if 語句,結(jié)尾肯定是有一個(gè) else 來完成,需要注意此點(diǎn);
- 在編寫流程控制時(shí)候,可以根據(jù)自己的習(xí)慣對(duì)于
command
中的操作進(jìn)行縮進(jìn),可以為幾個(gè)空格,也可以為一個(gè) tab 鍵,建議使用一個(gè)制表符; - 流程控制語句可以相互嵌套,例如 for 循環(huán)中可以有 if 條件判斷,if 添加判斷中也可以添加 for 循環(huán)等,根據(jù)需求具體靈活運(yùn)用;
- case 語句與 if 多分支語句一樣,不同點(diǎn)在與 case 語句只能判斷一種條件關(guān)系,而 if 多分支可以對(duì)多種條件進(jìn)行判斷。
5. 小結(jié)
流程控制語句是我們 Shell 編寫的經(jīng)絡(luò),利用它來控制程序的走向,需要我們能熟練掌握最常用的流程控制語句,并知道其對(duì)應(yīng)的適應(yīng)場景,靈活配合使用。