編寫Shell腳本
我經(jīng)常把Shell終端解釋器形容是人與計算機硬件的“翻譯官”绸罗,它作為用戶與Linux系統(tǒng)內(nèi)部通訊的媒介,除了允許了各種變量與參數(shù)外還提供了諸如循環(huán)豆瘫、分支等高級語言才有的控制結構特性珊蟀,如何正確的使用這些功能,準確下達命令尤為重要外驱。Shell腳本命令的工作方式有兩種育灸,首先是前面所接觸的交互方式(Interactive),即當用戶每輸入一條命令就執(zhí)行一次昵宇,而批處理(Batch)則是由用戶事先編寫好一個完整的Shell腳本磅崭,Shell會一次性執(zhí)行腳本中諸多的命令。因此在Shell腳本中不僅需要用到很多前面學習過的Linux命令以及正則表達式瓦哎、管道符砸喻、數(shù)據(jù)流重定向等語法規(guī)則,還需要把內(nèi)部功能模塊化后通過邏輯語句進行加工蒋譬,最終才能成為日常所見的Shell腳本程序割岛。
查看系統(tǒng)默認解釋器
可以通過SHELL變量來查看到當前系統(tǒng)已經(jīng)默認使用bash解釋器作為命令行終端了:
echo $SHELL
小試牛刀
需求:
想查看當前所在工作路徑并列出當前目錄下所有文件及屬性信息。
[centos7@localhost test]$ sudo vim example.sh
然后編輯內(nèi)容:
#!/bin/bash
# For Example BY jayafs
pwd
ls -al
Shell腳本文件的名稱是可以任意起犯助,但為了避免其他同事誤以為是普通文件蜂桶,咱們應該遵守行業(yè)人員大眾的規(guī)范把.sh后綴寫上,這樣讓其他人一看就知道是個腳本文件也切,與人方便自己方便扑媚。在這個腳本中實際上出現(xiàn)了三種不同的元素腰湾,第一行腳本聲明(#!)是用來告知系統(tǒng)用何種shell解釋器來執(zhí)行本腳本程序,第二行注釋信息(#)是對程序功能和某些命令的介紹信息疆股,使得自己或他人再次看到這個腳本內(nèi)容時可以快速知道這些功能的作用或一些警告信息费坊,第三、四行可執(zhí)行語句也就是咱們平時執(zhí)行的Linux命令啦
旬痹。這么簡單就編寫出來了一個腳本程序附井,那來執(zhí)行看一看吧:
第二種運行腳本程序的方法是以輸入完整路徑的方式來執(zhí)行,但默認會因為權限不足而提示報錯信息两残,這種情況只需要為腳本文件增加執(zhí)行權限即可永毅。
接受用戶參數(shù)
但是像上面這樣的腳本程序在功能上真的太過于“死板”,為了能夠讓Shell腳本程序更好的滿足用戶對靈活完成工作的熱切需要人弓,必須要讓腳本程序能夠像咱們以前執(zhí)行命令時那樣來接收用戶輸入進來的參數(shù)沼死。
其實Shell腳本早就考慮到了這些,已經(jīng)在腳本中定義好了很多變量功能崔赌,例如$0對應當前Shell腳本程序的名稱意蛀,$#對應總共有幾個參數(shù),$*對應所有位置的參數(shù)值健芭,而$1,$2,$3……依次類推則分別對應著第N個位置的參數(shù)县钥,如圖:
我們來實際操作下,修改example.sh
[centos7@localhost test]$ sudo vim example.sh
內(nèi)容為:
#!/bin/bash
echo "當前腳本名稱為$0"
echo "總共有$#個參數(shù)慈迈,分別是$*若贮。"
echo "第一個參數(shù)$1,第5個參數(shù)為$5痒留。"
運行腳本:
entos7@localhost test]$ sh example.sh one two three four five six
結果:
判斷語句
有時咱們也需要像mkdir
命令一樣來判斷用戶輸入的信息谴麦,從而判斷用戶指定的文件夾名稱是否已經(jīng)存在,已存在則提示報錯狭瞎,不存在則自動的創(chuàng)建细移。
條件判斷語句
條件測試語法能夠判斷表達式是否成立,若條件成立則返回數(shù)字0熊锭,否則便返回其他隨機數(shù)值弧轧。
格式
注意:
表達式在中括號里面,并且表達式前后都有一個空格
碗殷。
條件判斷語句按照測試對象可分為:
- 文件測試
- 邏輯測試
- 整數(shù)值比較
- 字符串比較
文件測試
文件測試即用來按照指定條件來判斷文件是否存在或權限是否滿足精绎。
參數(shù):
操作符 | 作用 |
---|---|
-d | 測試是否為目錄。 |
-e | 測試文件或目錄是否存在锌妻。 |
-f | 判斷是否為文件代乃。 |
-r | 測試當前用戶是否有權限讀取。 |
-w | 測試當前用戶是否有權限寫入。 |
-x | 測試當前用戶是否有權限執(zhí)行搁吓。 |
好啦原茅,那么先通過文件測試語句來判斷/etc/fstab是否為一個目錄文件,然后通過 $?
變量來顯示上一條命令執(zhí)行后的返回值堕仔,這樣就可以通過返回的非零值判斷目錄是不存在的了(即文件測試語句判斷結果不符合):
說明:
1擂橘、測試結果是不會直接輸出的、必須通過$?
來查看結果摩骨。
2通贞、如果檢測,測試成功返回 0恼五;反之返回非0(根據(jù)系統(tǒng)決定)昌罩。
邏輯測試
邏輯測試則是用于判斷用戶給出的條件是為真還是假,從而把條件測試語句與邏輯語句相搭配結合使用可以實現(xiàn)一個更高級的使用方法灾馒。
例如在Shell終端中邏輯“與”符號是&&茎用,它代表當前面的命令執(zhí)行成功后才會執(zhí)行后面的命令,因此可以用來判斷/dev/cdrom設備是否存在你虹,若存在時才輸出Exist字樣绘搞。
[centos7@localhost test]$ [ -e /dev/cdrom ] && echo "Exist"
Exist
除了“與”邏輯測試符號外還有“或”邏輯測試彤避,在Linux系統(tǒng)中的邏輯“或”符號為“||”瓤的,它代表當前面的命令執(zhí)行失敗后才會執(zhí)行后面的命令遥椿,因此可以結合系統(tǒng)環(huán)境變量USER來判斷當前登錄的用戶是否為非超級管理員身份:
[centos7@localhost test]$ echo $USER
centos7
[centos7@localhost test]$ [ $USER = root ] || echo "user"
user
除了基本的“與”、“或”邏輯符號外,還有邏輯“非”符號胚想,在Linux系統(tǒng)中邏輯“非”的符號就是一個嘆號,它代表把條件測試中的判斷結果取相反值吊输,也就是說原本測試的結果是正確声畏,則變成錯誤,而錯誤的結果會變成正確娄帖,有一種負負為正的感覺也祠。例如現(xiàn)在切換到一個普通用戶的身份后再來判斷當前用戶是不是一個非超級管理員的用戶,判斷結果因為兩次否定而變成正確近速,因此會正常的輸出預設信息:
[centos7@localhost test]$ [ $USER != centos7 ] || echo "administrator"
administrator
整數(shù)值比較
整數(shù)比較運算符是僅對數(shù)字的測試操作诈嘿,不能把數(shù)字與字符串、文件等內(nèi)容一起操作削葱,而且不能想當然的使用日常生活中的等號奖亚、大于號、小于號等來做判斷析砸,因為等號與是賦值命令符沖突昔字,大于號和小于號分別是和輸出重定向命令符和輸入重定向命令符沖突。雖然有時候碰巧也能執(zhí)行成功首繁,但是在后面腳本程序中普遍會產(chǎn)生錯誤作郭,一定要使用規(guī)范的整數(shù)比較運算符來進行操作陨囊。
參數(shù):
操作符 | 作用 |
---|---|
-eq | 判斷是否是等于 |
-ne | 判斷是否不等于 |
-gt | 判斷是否大于 |
-lt | 判斷是否小于 |
-le | 判斷是否等于或小于 |
-ge | 判斷是否大于或等于 |
小試牛刀
咱們先小試牛刀的測試下10是否大于10以及10是否等于10,依次通過判斷輸出的返回值內(nèi)容來進行判斷:
[centos7@localhost test]$ [ 10 -gt 10 ]
[centos7@localhost test]$ echo $?
1
注意
:
比較結果不會直接輸出夹攒,必須通過$?
來獲取谆扎。
字符串比較
字符串比較是判斷測試字符串是否為空值,或兩個字符串是否相同的操作芹助,常常用來判斷某個變量是否未被定義(即內(nèi)容為空值)堂湖。
常見的運算符:
操作符 | 作用 |
---|---|
= | 比較字符串內(nèi)容是否相同 |
!= | 比較字符串內(nèi)容是否不同 |
-z | 判斷字符串內(nèi)容是否為空 |
咱們可以通過判斷String變量是否為空值,進而判斷是否未被定義:
[centos7@localhost test]$ [ -z $String ]
[centos7@localhost test]$ echo $?
0
最后再嘗試把邏輯運算符引入來試試状土,當判斷用于保存當前語系的環(huán)境變量值LANG不是為英語(en.US)則會滿足邏輯條件并輸出非英語的字樣:
[centos7@localhost test]$ echo $LANG
en_US.UTF-8
[centos7@localhost test]$ [ $LANG != "en.US" ] && echo "Not en.US"
Not en.US
流程控制語句
前面學的判斷語句无蜂,這種腳本暫時并不能適用于日常生產(chǎn)環(huán)境的工作,首先是它不能根據(jù)實際工作內(nèi)容來調整具體的執(zhí)行命令內(nèi)容蒙谓,也不能根據(jù)某些條件來實現(xiàn)自動循環(huán)執(zhí)行斥季,例如需要批量的創(chuàng)建一千個用戶,首先您要能判斷這些用戶是否已經(jīng)存在了累驮,若不存在然后再通過循環(huán)語句讓腳本自動化的依次創(chuàng)建他們酣倾。
語句:
- if
- for
- while
- case
if條件測試語句
if條件語句可以讓腳本根據(jù)實際情況的不同而自動切換命令執(zhí)行方案。
- 單分支
- 雙分支
- 多分支
單分支
單分支的if條件語句結構谤专,這種結構僅用if躁锡、then、fi關鍵詞組成置侍,只在條件成立后才執(zhí)行預設命令映之,相當于口語的“如果……那么……”,屬于最簡單的一種條件判斷結構蜡坊,操作語法:
需求:
使用單分支的if條件語句來判斷某個目錄是否存在杠输,若已經(jīng)存在就結束條件判斷和整個Shell腳本,而如果不存在則去創(chuàng)建這個目錄秕衙。
[root@linuxprobe ~]# vim mkcdrom.sh
#!/bin/bash
DIR="/media/cdrom"
if [ ! -e $DIR ]
then
mkdir -p $DIR
fi
執(zhí)行腳本:
[centos7@localhost test]$ bash mkcdrom.sh
[centos7@localhost test]$ ls -d /media/cdrom
/media/cdrom
注意:
if后面接判斷條件蠢甲,中括號中字符串前后一定要有空格
。
雙分支
雙分支的if條件語句結構据忘,這種結構僅用if鹦牛、then、else若河、fi關鍵詞組成能岩,進行兩次條件判斷匹配,兩次判斷中任何一項匹配成功后都會執(zhí)行預設命令萧福,相當于口語的“如果……那么……或者……那么……”拉鹃。
操作語法:
使用雙分支的if條件語句來驗證某個主機是否在線,然后根據(jù)判斷執(zhí)行返回值結果分別給予對方主機是在線還是不在線的提示信息。腳本中我主要是使用ping命令來測試與對方主機的網(wǎng)絡聯(lián)通性膏燕,而linux系統(tǒng)中的ping命令不像windows系統(tǒng)一樣僅會嘗試四次就結束钥屈,因此為了避免用戶等待時間過長,而通過-c參數(shù)來規(guī)定嘗試的次數(shù)坝辫,-i參數(shù)定義每個數(shù)據(jù)包的發(fā)送間隔時間以及-W參數(shù)定義最長的等待超時時間篷就。
[root@linuxprobe ~]# vim chkhost.sh
#!/bin/bash
ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
if [ $? -eq 0 ]
then
echo "Host $1 is On-line."
else
echo "Host $1 is Off-line."
fi
若上一條語句是順利執(zhí)行成功的則會返回數(shù)字0,而若上一條語句執(zhí)行是失敗的則返回一個非零的數(shù)字(隨系統(tǒng)版本差異可能會是1或者2都有可能)近忙,因此可以通過用數(shù)字條件測試的方法判斷$?變量是否等于零來獲知上一條語句的最終判斷情況竭业,192.168.10.10是服務器本機地址,驗證下腳本的效果吧:
[centos7@localhost test]$ bash chkhost.sh 192.168.10.10
Host 192.168.10.10 is Off-line
分析:
linux系統(tǒng)中的ping命令不像windows系統(tǒng)一樣僅會嘗試四次就結束及舍。它接受對應胡參數(shù):
- -c:規(guī)定嘗試次數(shù)
- -i:每個數(shù)據(jù)包的發(fā)送間隔時間
- -W:最長等待超時時間
多分支
多分支的if條件語句結構未辆,這種結構需要使用if、then锯玛、else咐柜、elif、fi關鍵詞組成攘残,進行多次條件判斷匹配拙友,多次判斷中任何一項匹配成功后都會執(zhí)行預設命令,相當于口語的“如果……那么……如果……那么……N次等等”歼郭,這是一種工作中最常使用的條件判斷結構遗契,雖然相對復雜但更加靈活。
操作語法:
使用多分支的if條件語句來判斷用戶輸入的分數(shù)在那個成績區(qū)間內(nèi)实撒,然后輸出如優(yōu)秀姊途、合格涉瘾、不合格等提示信息知态。read是用來讀取用戶輸入信息的命令,它能夠把接收到的用戶輸入信息賦值給后面的指定變量立叛,而-p參數(shù)則是給予了用戶一定的提示信息负敏。下面實例中判斷用戶輸入的分數(shù)是否同時具備大于等于85分且小于等于100分,這樣的話才輸出Excellent字樣秘蛇,若上一條件沒有匹配成功則繼續(xù)判斷用戶輸入分數(shù)是否大于等于70分且小于等于84分其做,這樣的話輸出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 "$GRADE is Excellent"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] ; then
echo "$GRADE is Pass"
else
echo "$GRADE is Fail"
fi
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):88
88 is Excellent
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):80
80 is Pass
如果用戶輸入的分數(shù)并沒有滿足第一項匹配條件妖泄,則會自動進行下面的匹配流程:
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):30
30 is Fail
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):200
200 is Fail
您會不會很奇怪,為什么我輸入200分的超高成績卻依然提示了Fail字樣艘策?其實原因是很簡單明顯的蹈胡,咱們的條件判斷語句兩條都沒有匹配成功,因此自動執(zhí)行了最終的兜底策略。
此腳本不是很完美罚渐,還可以所有大于100分或小于0分的用戶輸入都設置提示下Error字樣却汉。
for條件循環(huán)語句
for循環(huán)語句可以讓腳本一次性讀取多個信息值,然后逐一對信息值進行循環(huán)操作處理荷并,因此當您要處理的數(shù)據(jù)是有目標和范圍時簡直再適合不過了合砂。
處理流程:
例如使用for循環(huán)語句來從列表文件中讀取多個用戶名,然后逐一創(chuàng)建用戶帳號并為其設置密碼源织。
首先創(chuàng)建用戶名稱的列表文件翩伪,把每個用戶名稱單獨占一行,當然具體的用戶名稱和個數(shù)都是可以由自己來決定的:
[root@linuxprobe ~]# vim users.txt
andy
barry
carl
duke
eric
george
Shell腳本中使用read命令來讀取用戶輸入的密碼值后賦值給PASSWD變量谈息,并通過-p參數(shù)來顯示一段給用戶的提示內(nèi)容幻工,告訴用戶正在輸入的內(nèi)容即將作為帳號密碼。當下面的腳本執(zhí)行后會自動的用users.txt列表文件中獲取到所有的用戶名稱值黎茎,然后逐一使用id 用戶名的方式查看用戶的信息囊颅,并使用$?變量判斷這條命令是否執(zhí)行成功,也就是判斷該用戶是否已經(jīng)存在傅瞻。而/dev/null是被稱作Linux的黑洞的文件踢代,把輸出信息重定向到這個文件后等同于刪除數(shù)據(jù)(沒有回收功能的垃圾箱),讓用戶的屏幕窗口保持簡潔嗅骄。
[root@linuxprobe ~]# vim Example.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
執(zhí)行批量創(chuàng)建用戶的Shell腳本程序胳挎,在輸入為帳戶設定的密碼口令后將由腳本全自動的檢查并創(chuàng)建這些帳號,因為已經(jīng)把多余的信息通過輸出重定向轉移到了黑洞文件中溺森,因此屏幕窗口除了用戶創(chuàng)建成功的提示后不會有其他的內(nèi)容慕爬。/etc/passwd是用來保存Linux系統(tǒng)中用戶帳號信息的文件,因此如果不放心的話可以手動的再看下這個文件中有無這些新用戶的信息屏积,同時這也又回歸到了前面3章中重復提到了一個概念——Linux系統(tǒng)中的一切都是文件医窿。
[root@linuxprobe ~]# bash Example.sh
Enter The Users Password : linuxprobe
andy , Create success
barry , Create success
carl , Create success
duke , Create success
eric , Create success
george , Create success
[root@linuxprobe ~]# tail -6 /etc/passwd
andy:x:1001:1001::/home/andy:/bin/bash
barry:x:1002:1002::/home/barry:/bin/bash
carl:x:1003:1003::/home/carl:/bin/bash
duke:x:1004:1004::/home/duke:/bin/bash
eric:x:1005:1005::/home/eric:/bin/bash
george:x:1006:1006::/home/george:/bin/bash
while條件循環(huán)語句
這是一種讓腳本根據(jù)某些條件來重復執(zhí)行命令的條件循環(huán)語句,而這種循環(huán)結構往往在執(zhí)行前并不確定最終執(zhí)行的次數(shù)炊林,完全不同于for循環(huán)語句中有目的姥卢、有范圍的使用場景。而while循環(huán)語句判斷是否繼續(xù)執(zhí)行命令的依據(jù)一般是檢查若條件為真就繼續(xù)執(zhí)行渣聚,而條件為假就結束循環(huán)独榴。
結構:
接下來就來利用多重分支的if條件測試語句與while條件循環(huán)語句來結合寫一個用來判斷數(shù)值的腳本吧,腳本中會使用$RANDOM變量來調取出一個隨機的數(shù)值(范圍:0--32767)奕枝,然后通過expr命令計算取整出1000以內(nèi)的一個隨機數(shù)值棺榔,用這個數(shù)值來跟用戶通過read命令輸入的數(shù)值做比較判斷。判斷語句結構分為三項隘道,分別是判斷是否相等症歇、是否大于隨機值以及是否小于隨機值捞烟,但這不是重點~關鍵是在于while條件循環(huán)語句的判斷值為true,因此會無限的運行下去当船,直到猜中后運行exit 0命令才終止腳本题画。
[root@linuxprobe ~]# vim Guess.sh
#!/bin/bash
PRICE=$(expr $RANDOM % 1000)
TIMES=0
echo "商品實際價格為0-999之間,猜猜看是多少德频?"
while true
do
read -p "請輸入您猜測的價格數(shù)目:" INT
let TIMES++
if [ $INT -eq $PRICE ] ; then
echo "恭喜您答對了苍息,實際價格是 $PRICE"
echo "您總共猜測了 $TIMES 次"
exit 0
elif [ $INT -gt $PRICE ] ; then
echo "太高了!"
else
echo "太低了壹置!"
fi
done
通過給腳本加上解釋說明后整個內(nèi)容開始變得豐滿起來竞思,互動感也變得很強,每當循環(huán)到let TIMES++這個命令時都會讓TIMES變量內(nèi)數(shù)值加上1钞护,這樣用來統(tǒng)計總共循環(huán)次數(shù)的功能更是畫龍點睛盖喷,讓操作者可以知道猜對價格最終使用了幾次機會。
[root@linuxprobe ~]# bash Guess.sh
商品實際價格為0-999之間难咕,猜猜看是多少课梳?
請輸入您猜測的價格數(shù)目:500
太低了!
請輸入您猜測的價格數(shù)目:800
太高了余佃!
請輸入您猜測的價格數(shù)目:650
太低了暮刃!
請輸入您猜測的價格數(shù)目:720
太高了!
請輸入您猜測的價格數(shù)目:690
太低了爆土!
請輸入您猜測的價格數(shù)目:700
太高了椭懊!
請輸入您猜測的價格數(shù)目:695
太高了!
請輸入您猜測的價格數(shù)目:692
太高了步势!
請輸入您猜測的價格數(shù)目:691
恭喜您答對了氧猬,實際價格是 691
您總共猜測了 9 次
case條件測試語句
如果您學習過C語言,此刻一定是會心一笑坏瘩,這不就是switch語句嗎盅抚?是的,功能非常相似桑腮!case條件測試語句是在多個范圍內(nèi)匹配數(shù)據(jù)泉哈,若匹配到則執(zhí)行相關命令并結束整個條件測試,而如果數(shù)據(jù)不在所列出的范圍內(nèi)破讨,則會去執(zhí)行*)中所規(guī)定的默認命令。
結構:
剛剛學習的腳本普遍有一個致命的弱點奕纫,不信您就輸入一個字母或亂碼試一試腳本立即就崩潰了提陶。這是由于字母是不能跟數(shù)字做大小比較的,例如a是否大于等于3匹层,這樣的命題完全錯誤隙笆,變量操作會直接導致系統(tǒng)崩潰锌蓄。咱們必須馬上想出一個辦法來判斷用戶的輸入內(nèi)容,一旦碰到字母或亂碼也能予以提示撑柔,不至于因錯誤輸入而崩潰瘸爽,因此這樣的需求用case條件測試語句和第3章節(jié)中學習的通配符來一起組合寫一個腳本簡直再適合不過了提示用戶輸入一個字符并將其賦值給變量KEY,判斷變量KEY為何種字符后分別輸出是字母铅忿、數(shù)字還是其他字符:
[root@linuxprobe ~]# vim Checkkeys.sh
#!/bin/bash
read -p "請輸入一個字符剪决,并按Enter鍵確認:" KEY
case "$KEY" in
[a-z]|[A-Z])
echo "您輸入的是 字母。"
;;
[0-9])
echo "您輸入的是 數(shù)字檀训。"
;;
*)
echo "您輸入的是 空格柑潦、功能鍵或其他控制字符。"
esac
[root@linuxprobe ~]# bash Checkkeys.sh
請輸入一個字符峻凫,并按Enter鍵確認:6
您輸入的是 數(shù)字渗鬼。
[root@linuxprobe ~]# bash Checkkeys.sh
請輸入一個字符,并按Enter鍵確認:p
您輸入的是 字母荧琼。
[root@linuxprobe ~]# bash Checkkeys.sh
請輸入一個字符譬胎,并按Enter鍵確認:^[[15~
您輸入的是 空格、功能鍵或其他控制字符命锄。