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