Shell腳本命令的作業方式有兩種:
互動式(Interactive):用戶每輸入一條命令就立即執行,
批處理(Batch):由用戶事先撰寫好一個完整的Shell腳本,Shell會一次性執行腳本中諸多的命令,
一、撰寫簡單的腳本
一個Shell腳本主要由三部分組成:腳本宣告、腳本注釋、腳本命令,
- 腳本宣告:告訴系統使用哪種Shell解釋器來執行該腳本,比如:#!/bin/bash
- 腳本注釋:以#開頭,主要是介紹腳本的功能和某些命令
- 腳本命令:需要被執行的Linux命令,
Shell腳本的名稱可以任意,但為了方便用戶辨認,建議加上.sh后綴以表示這是一個腳本檔案,下面通過Vim編輯器簡單撰寫一個Shell腳本:
[root@linuxprobe ~]# vim example.sh
#!/bin/bash //腳本宣告
# for example by xuliang //腳本注釋
pwd //腳本命令
ls -al
可以通過bash命令直接運行腳本檔案,也可以通過輸入完整路徑的方式來執行,但是需要先對腳本檔案添加可執行權限,
[root@linuxprobe ~]# bash example.sh //通過bash命令執行腳本檔案
/root
total 21260
dr-xr-x---. 17 root root 4096 Feb 23 16:57 .
drwxr-xr-x. 17 root root 4096 Feb 23 10:34 ..
drwxr-xr-x. 3 root root 14 Feb 18 15:26 a
-rw-------. 1 root root 1032 Feb 18 2019 anaconda-ks.cfg
-rw-------. 1 root root 6039 Feb 23 10:57 .bash_history
---------------------省略部分輸出內容------------------------------
[root@linuxprobe ~]# ./example.sh //通過完整路徑執行腳本檔案,需要可執行權限
-bash: ./example.sh: Permission denied
[root@linuxprobe ~]# chmod u+x example.sh //添加可執行權限
[root@linuxprobe ~]#
[root@linuxprobe ~]# ./example.sh //腳本執行成功
/root
total 21260
dr-xr-x---. 17 root root 4096 Feb 23 16:57 .
drwxr-xr-x. 17 root root 4096 Feb 23 10:34 ..
drwxr-xr-x. 3 root root 14 Feb 18 15:26 a
-rw-------. 1 root root 1032 Feb 18 2019 anaconda-ks.cfg
-rw-------. 1 root root 6039 Feb 23 10:57 .bash_history
-------------------省略部分輸出內容-----------------------------
二、接收用戶的引數
上面的腳本只能執行一些預先定義好的命令,未免太過于死板了,為了增加Shell腳本的靈活性,必須讓腳本可以接收用戶輸入的引數,Linux系統中的Shell腳本語言已經內設了用于接受引數的變數,變數之間使用空格間隔,相關變數如下所示:
- $0:表示當前Shell腳本的名稱;
- $#:表示總共有幾個引數;
- $*:表示所有位置的引數值
- $1、$2、$3、$4.....:表示第N個位置的引數值,

“百聞不如一見,看書不如實踐”,接下來通過撰寫一個腳本,參考上面的變數引數來看一下實際效果:
[root@linuxprobe ~]# vim example.sh
#!/bin/bash
# for example by xuliang
echo "當前腳本名稱$0"
echo "總共有$#個引數,分別是$*"
echo "第一個引數為$1,第3為$3"
[root@linuxprobe ~]# bash example.sh 1 2 3 4 5 6 //傳入6個引數
當前腳本名稱example.sh
總共有6個引數,分別是1 2 3 4 5 6
第一個引數為1,第3為3
三、判斷用戶的引數
接下來學習如何處理接收到的用戶引數,Shell腳本中的條件測驗語法可以判斷運算式是否成立,若條件成立則回傳數字0,否則回傳其他亂數字(一般都是回傳1),條件測驗陳述句的執行格式如下所示,切記,條件運算式兩邊必須要有一個空格,
按照測驗物件來劃分,條件測驗陳述句可以分為4種:
- 檔案測驗陳述句;
- 邏輯測驗陳述句;
- 整數值比較陳述句;
- 字串比較陳述句,
1、檔案測驗陳述句
指使用指定條件來判斷檔案是否存在或權限是否滿足等情況的運算子,具體引數如下所示:
| 運算子 | 作用 |
| -d | 測驗檔案是否為目錄型別 |
| -e | 測驗檔案是否存在 |
| -f | 判斷是否為一般檔案 |
| -r | 測驗當前用戶是否有權限讀取 |
| -w | 測驗當前用戶是否有權限寫入 |
| -x | 測驗當前用戶是否有權限執行 |
實驗1:使用檔案測驗陳述句判斷/etc/fstab是否為一個目錄型別檔案,然后通過Shell解釋器的內設$?變數來顯示上一條命令執行后的回傳值,如果上一條命令執行成功,則$?變數的數值為0;反之則為一個非零值(一般都是1),
[root@linuxprobe ~]# [ -d /etc/fstab ]
[root@linuxprobe ~]# echo $? //顯示上條命令執行結果
1 //非零值表示執行失敗
[root@linuxprobe ~]#
實驗2:使用條件測驗陳述句判斷/etc/fstab是否為一個一般檔案,
[root@linuxprobe ~]# [ -f /etc/fstab ]
[root@linuxprobe ~]# echo $?
0 //執行成功
[root@linuxprobe ~]#
2、邏輯測驗陳述句
指對測驗結果進行邏輯分析,根據測驗結果實作不同的效果,主要有3種邏輯運算子,如下所示:
| 運算子 | 作用 |
| 邏輯與(&&) | 表示前面的命令執行成功后,才執行后面的命令 |
| 邏輯或(||) | 表示前面的命令執行失敗后,才執行后面的命令 |
| 邏輯非(!) | 表示把條件測驗陳述句中的判斷結果取相反值 |
實驗1:判斷/dev/cdrom檔案是否存在,若存在則輸出Exist字樣,
[root@linuxprobe ~]# [ -e /dev/cdrom ] && echo "Exist" //邏輯與
Exist
實驗2:判斷當前登錄的用戶是否為管理員身份:
[root@linuxprobe ~]# su - linuxprobe //切換至linuxprobe用戶
Last login: Mon Feb 24 10:26:51 BNT 2020 on pts/1
[linuxprobe@linuxprobe ~]$
[linuxprobe@linuxprobe ~]$ [ $USER = root ] || echo "not root" //邏輯或
not root
實驗3:判斷當前登錄用戶為非管理員身份:
[linuxprobe@linuxprobe ~]$ su - root //切換至root用戶
Password:
Last login: Mon Feb 24 11:13:05 BNT 2020 on pts/1
[root@linuxprobe ~]# [ ! $USER = root ] || echo "It's root" //邏輯非
It's root
實驗4:判斷當前登錄的用戶,若是普通用戶則輸出“user”,若是管理員用戶則輸出“root”
[root@linuxprobe ~]# [ ! $USER = root ] && echo "user" || echo "It's root"
It's root //root用戶
[root@linuxprobe ~]# su - linuxprobe //切換至linuxprobe用戶
Last login: Mon Feb 24 14:41:29 BNT 2020 on pts/1
[linuxprobe@linuxprobe ~]$ [ ! $USER = root ] && echo "user" || echo "It's root"
user //普通用戶
3、整數比較運算子
僅僅是對數字的操作,即運算子的兩邊必須是數字,不能將數字與字串、檔案等內容一起操作,而且一定要使用規范的整數比較運算子來進行操作,整數比較運算子如下所示:
| 運算子 | 作用 |
| -eq | 是否等于 |
| -ne | 是否不等于 |
| -gt | 是否大于(greater than) |
| -lt | 是否小于(less than) |
| -le | 是否等于或小于 |
| -ge | 是否大于或等于 |
實驗:判斷當前主機空閑記憶體是否小于1024M,若小于1024M,則輸出“Insufficient Memory”的字樣,
[linuxprobe@linuxprobe ~]$ FreeMem=`free -m | grep Mem: | awk '{print $4}'` //獲取當前主機空閑記憶體值,注意賦值號的兩邊不能有空格
[linuxprobe@linuxprobe ~]$
[linuxprobe@linuxprobe ~]$ [ $FreeMem -lt 1024 ] && echo "Insuficient Memory"
Insuficient Memory
4、字串比較陳述句
用于判斷測驗字串是否為空值,或兩個字串是否相同,常見的字串運算子如下所示:
| 運算子 | 作用 |
| = | 比較字串的內容是否相同 |
| != | 比較字串的內容是否不同 |
| -z | 判斷字串的內容是否為空 |
實驗1:判斷變數String是否空值,即判斷是否定義了該變數,
[linuxprobe@linuxprobe ~]$ [ -z $String ]
[linuxprobe@linuxprobe ~]$ echo $?
0
實驗2:判斷當前LANG環境變數值是否為“en.US”,
[linuxprobe@linuxprobe ~]$ echo $LANG
en_US.UTF-8
[linuxprobe@linuxprobe ~]$ [ $LANG = "en.US" ] || echo "Not en.US"
Not en.US
四、流程控制陳述句
在Shell腳本中,我們可以通過if、for、while、case這4中流程控制陳述句來撰寫難度更大、功能更強的腳本,來匹配實際的生產需求,
1、if條件測驗陳述句
if條件測驗陳述句分為單分支結構、雙分支結構、多分支結構,下面逐一進行介紹:
(1)單分支結構
由if、then、fi關鍵詞組成,只有在判斷條件成立之后才執行預設的命令,相當于口語的“如果........那么........”,語法格式如下所示:

實驗:判斷/root/test目錄檔案是否存在,如存在則結束Shell腳本,否則創建該目錄,
[root@linuxprobe ~]# vim mkcdrom.sh
#!/bin/bash
DIR="/root/test"
if [ ! -e $DIR ]
then
mkdir -p $DIR
fi
[root@linuxprobe ~]# bash mkcdrom.sh //執行腳本
[root@linuxprobe ~]# ls -d /root/test/ //檢查目錄是否創建成功
/root/test/
(2)多分支結構
由if、then、else、fi關鍵詞組成,它進行一次條件匹配判斷,若匹配成功,則執行預設的命令,否則去執行匹配失敗時的預設命令,語法格式如下所示:

實驗:用戶自行輸入IP地址,并判斷該主機是否在線,
[root@linuxprobe ~]# vim chkhost.sh
#!/bin/bash
ping -c 3 -i 0.2 -w 3 $1 &> /dev/null ###其中引數表示ping3次、每次間隔0.2秒、等待超時時間為3秒
if [ $? -eq 0 ]
then
echo "Host $1 is On-line"
else
echo "Host $1 is Off-line"
fi
[root@linuxprobe ~]# bash chkhost.sh 192.168.134.10
Host 192.168.134.10 is On-line //主機在線
[root@linuxprobe ~]# bash chkhost.sh 192.168.134.20
Host 192.168.134.20 is Off-line //主機不在線
[root@linuxprobe ~]#
(3)多分支結構
由if、then、else、elif、fi關鍵詞組成,它進行多次條件匹配判斷,這么多次判斷中的任一項匹配成功都會執行相應的預設命令,相當于口語的“如果.....那么......如果.......那么.....”,多分支陳述句可以多次嵌套,語法格式如下所示:

實驗:判斷用戶輸入的分數在哪個區間,然后輸出Excellent、Pass、Fail等提示資訊,
[root@linuxprobe ~]# vim chkscore.sh
#!/bin/bash
read -p "Enter your score(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]
then
echo "Excellent"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ]
then
echo "Pass"
else
echo "Fail"
fi
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):88
Excellent
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):73
Pass
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):60
Fail
2、for條件回圈陳述句
for回圈陳述句允許腳本一次性讀取多個資訊,然后逐一對資訊進行操作處理,當要處理有范圍的資料時,使用for回圈陳述句再適合不過了,其語法格式如下所示:

實驗1:從串列檔案中讀取多個用戶名,然后為其逐一創建用戶賬號并設定密碼,
[root@linuxprobe ~]# vim users.txt //首先創建包含用戶名稱的檔案
zhangsan
lisi
wangwu
zhaoliu
[root@linuxprobe ~]# vim CreateUser.sh //撰寫腳本
#!/bin/bash
read -p "Enter the Users Password:" PASSWD
for UNAME in `cat users.txt`
do
id $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "Already Exists"
else
useradd $UNAME &> /dev/null ##添加用戶
echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "$UNAME create success"
else
echo "$UNAME create failure"
fi
fi
done
[root@linuxprobe ~]# bash CreateUser.sh //執行腳本
Enter the Users Password:123456
zhangsan create success
lisi create success
wangwu create success
zhaoliu create success
[root@linuxprobe ~]#
補充:假如用戶名檔案users.txt內容寫成以下形式(即所有用戶名寫成一行,用空格分開),CreateUser.sh腳本同樣執行成功,
[root@linuxprobe ~]# cat users.txt
andy barry carl
[root@linuxprobe ~]#
[root@linuxprobe ~]# bash CreateUser.sh
Enter the Users Password:123456
andy create success
barry create success
carl create success
實驗2:讀取檔案中的主機串列,然后逐個測驗這些主機是否在線,
[root@linuxprobe ~]# vim ipaddrs.txt //創建主機串列檔案
192.168.134.10
192.168.134.20
192.168.134.30
192.168.134.40
[root@linuxprobe ~]# vim chkhosts.sh //撰寫腳本
#!/bin/bash
HLIST=$(cat ~/ipaddrs.txt) //其中$(命令)相當于`命令`
for IP in $HLIST
do
ping -c 3 -i 0.2 -w 3 $IP &> /dev/null
if [ $? -eq 0 ] ; then
echo "$IP is On-line"
else
echo "$IP is Off-line"
fi
done
[root@linuxprobe ~]# bash chkhosts.sh //運行腳本
192.168.134.10 is On-line
192.168.134.20 is Off-line
192.168.134.30 is Off-line
192.168.134.40 is Off-line
3、while條件回圈陳述句
while條件陳述句是一種讓腳本根據某些條件來重復執行命令的陳述句,它的回圈結構往往在執行前并不確定最終執行的次數,它通過判斷條件測驗的真偽來決定是否繼續執行命令,條件為真就繼續執行,為假就結束回圈,語法結構如下所示:

實驗:撰寫一個猜數的腳本,若用戶輸入的數值與腳本隨機生成的數值一致,則結束游戲,
[root@linuxprobe ~]# vim guess.sh
#!/bin/bash
PRICE=$(expr $RANDOM % 1000) ##獲取一個1000以內的亂數
TIMES=0
echo "商品的實際價格為0-999之間,請猜猜看是多少?"
while true
do
read -p "請輸入你猜測的價格:" INT
let TIMES++ ##TIMES變數自增1
if [ $INT -eq $PRICE ] ; then
echo "恭喜你猜對啦,實際價格為 $PRICE"
echo "您總共猜了 $TIMES 次"
exit
elif [ $INT -gt $PRICE ] ; then
echo "您猜高了"
else
echo "您猜低了"
fi
done
[root@linuxprobe ~]# bash guess.sh
商品的實際價格為0-999之間,請猜猜看是多少?
請輸入你猜測的價格:500
您猜低了
請輸入你猜測的價格:800
您猜高了
-------------------省略部分內容---------------------
您猜高了
請輸入你猜測的價格:516
您猜低了
請輸入你猜測的價格:517
恭喜你猜對啦,實際價格為 517
您總共猜了 14 次
4、case條件測驗陳述句
case陳述句是在多個范圍內匹配資料,若匹配成功則執行相關命令并結束整個條件測驗;如果資料在所列的范圍內,則會去執行星號(*)中所定義的默認命令,語法結構如下所示:

實驗:判斷用戶輸入的資料是字母、數字還是其他字符,
[root@linuxprobe ~]# cat chkkeys.sh
#!/bin/bash
read -p "請輸入一個字符:" KEY
case "$KEY" in
[a-z]|[A-Z])
echo "您輸入的是字母"
;;
[0-9])
echo "您輸入的是數字"
;;
*)
echo "您輸入的是特殊字符"
esac
[root@linuxprobe ~]# bash chkkeys.sh
請輸入一個字符:2
您輸入的是數字
[root@linuxprobe ~]# bash chkkeys.sh
請輸入一個字符:s
您輸入的是字母
[root@linuxprobe ~]# bash chkkeys.sh
請輸入一個字符:`
您輸入的是特殊字符
[root@linuxprobe ~]#
補充:上述關于檢查字符的腳本是有缺陷的,當用戶輸入兩位及以上的數字或字母時,會提示輸入的是特殊字符,
[root@linuxprobe ~]# bash chkkeys.sh
請輸入一個字符:12
您輸入的是特殊字符
解決方法:修改匹配條件,如下所示,但是當輸入三位數字時,依然提示輸入的是特殊字符,需要繼續修改匹配條件,所以該方法只能算打補丁,不能算真正的解決方法,
[root@linuxprobe ~]# cat chkkeys.sh
#!/bin/bash
read -p "請輸入一個字符:" KEY
case "$KEY" in
[a-z]|[A-Z])
echo "您輸入的是字母"
;;
[0-9]|[0-9][0-9]) ##匹配一位或兩位數字
echo "您輸入的是數字"
;;
*)
echo "您輸入的是特殊字符"
esac
[root@linuxprobe ~]# bash chkkeys.sh
請輸入一個字符:34
您輸入的是數字
四、計劃任務服務程式
在實際的運維作業中,需要在指定的時間段自動啟動或停止某些服務或命令,從而實作運維的自動化,接下來介紹如何設定服務器的計劃任務服務,把周期性、規律性的作業交給系統自動完成,
計劃任務分為一次性任務和長期性任務,如下所示:
- 一次性計劃任務:比如今晚11點30分開啟網站服務,
- 長期性計劃任務:比如每周一的凌晨3點30分把/home/www目錄的檔案打包備份為backup.tar.gz,
1、一次性計劃任務
顧名思義,一次性計劃任務只執行一次,一般用于滿足臨時的作業需求,可以用at命令實作這種功能,相關功能如下所示:
- at 時間 :表示創建一個一次性計劃任務,
- at -l :表示查看已經設定好但未執行的一次性計劃任務,
- atrm 任務序號 : 表示洗掉一個一次性計劃任務,
實驗1:設定在今天11:24重啟系統主機
[root@linuxprobe ~]# at 11:24
at> reboot
at> <此處按下Ctrl+d組合鍵來結束撰寫計劃任務>
job 2 at Sat Feb 29 11:24:00 2020
[root@linuxprobe ~]# at -l
2 Sat Feb 29 11:24:00 2020 a root
[root@linuxprobe ~]#
實驗2:at命令默認采用互動式的方式,接下使用非互動式的方法創建一個一次性計劃任務,
[root@linuxprobe ~]# echo "systemctl restart network" | at 11:35
job 4 at Sat Feb 29 11:35:00 2020
[root@linuxprobe ~]#
[root@linuxprobe ~]# at -l
4 Sat Feb 29 11:35:00 2020 a root //其中"4"就是任務序號
實驗3:洗掉一個一次性計劃任務,
[root@linuxprobe ~]# atrm 4
[root@linuxprobe ~]# at -l
[root@linuxprobe ~]#
2、長期性計劃任務
Linux系統中默認啟動的crond服務能夠周期性地、有規律地執行某些具體的任務,相關地命令如下所示:
| 命令 | 作用 |
| crontab -e | 創建、編輯計劃任務 |
| crontab -l | 查看當前地計劃任務 |
| crontab -r | 洗掉某條計劃任務 |
| crontab -u | 編輯他人的計劃任務(必須是root身份) |
使用crond服務設定計劃任務時,語法格式為“分 時 日 月 星期 命令”,需要注意的是,如果有些欄位沒有設定,則必須使用星號(*)占位,如下圖所示:

使用crond設定任務的引數欄位說明,如下表所示:
| 欄位 | 說明 |
| 分 | 取值為0~59的整數 |
| 時 | 取值為0~23的任意整數 |
| 日 | 取值為0~31的任意整數 |
| 月 | 取值1~12的任意整數 |
| 星期 | 取值0~7的任意整數,其中0與7均為星期日 |
| 命令 | 要執行的命令或腳本(必須要用絕對路徑來寫) |
實驗1:設定在每周一、三、五的凌晨3點25分,使用tar命令打包網站的資料,
[root@linuxprobe ~]# crontab -e //默認進入Vim編輯器界面
no crontab for root - using an empty one
crontab: installing new crontab
[root@linuxprobe ~]# crontab -l
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
[root@linuxprobe ~]#
補充說明:這里補充幾點關于時間周期的設定,如下所示:
- 用逗號(,)來表示多個時間段,如“星期”欄位“1,3,5”表示周一、周三和周五,
- 用減號(-)來表示一段連續的時間周期,如“月”欄位“8-12”表示8~12月,
- 用除號(/)來表示執行任務的間隔時間,如“分”欄位“*/2”表示每隔2分鐘執行一次任務,
crond服務可以包含多條計劃任務,注意每條計劃任務占一行,比如我們要增加一條計劃任務,它的功能是每周一至周五的凌晨1點自動清空/tmp目錄內的所有檔案,
[root@linuxprobe ~]# whereis rm //使用whereis命令查詢絕對路徑
rm: /usr/bin/rm /usr/share/man/man1/rm.1.gz /usr/share/man/man1p/rm.1p.gz
[root@linuxprobe ~]#
[root@linuxprobe ~]# crontab -e
crontab: installing new crontab
[root@linuxprobe ~]# crontab -l
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
0 1 * * 1-5 /usr/bin/rm -rf /tmp/*
注意事項:
(1)在Vim編輯器中配置計劃任務時,可以以#號開頭寫上注釋資訊,
(2)計劃任務中的“分”欄位必須有數值,絕對不能為慷訓*號,
(3)“日”和“星期”欄位不能同時使用,否則會發生沖突,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/138742.html
標籤:Linux
