Bash shell 腳本編程全攻略(上篇)
什么是shell腳本呢?
當命令不在命令行上執行,而是通過一個文件執行時,該文件就稱為shell腳本,腳本以非交互的方式運行。Shell腳本把命令通過一些語法組織起來,便能實現特定的功能。
Shell腳本主要運用在系統運維中,主要功能有:
自動化常用命令;
執行系統管理和故障排除;
創建簡單的應用程序;
處理文本或文件。
Shell腳本創建與執行
Shell腳本通常在編輯器中編寫,由命令及其注釋組成,注釋是跟在井號(#)后面的內容,用來對腳本進行注釋。
腳本左上角的第一行會指出由哪個程序來執行腳本中的行。這一行通常稱為shbang行,必須為頂端第一行,如由bash來執行則為:
#!/bin/bash
在執行shell腳本時,須先給予執行權力,然后在命令行上指定腳本的路徑運行;或是直接運行解釋器,將腳本作為解釋器的參數運行。
了解shell腳本編程,我們先要了解其一些基本的組成或涉及的基本內容。由于涉及內容較多,一些部分可能不會詳細講解。
變量
學習Shell腳本離不開變量,根據生效范圍變量主要分為;
局部變量:只對當前shell進程有效;對當前shell之外的其它shell進程包括其子shell均無效。在函數中用local聲明的局部變量只能用在函數中。
環境變量:當前shell進程及其子進程有效。
變量賦值
局部變量 VAR_NAME=value 或declare VAR_NAME=value
環境變量 export name=value 或declare -x name=value
顯示所有環境變量可以用命令:export或env
只讀變量 readonly name=value 或 delcare -r name=value
聲明后不能修改和刪除。
刪除變量 unset name
顯示已定義的所有變量 set或declare
變量引用 $name;${name}
shell 會對雙引號內的美元符號后的變量執行變量擴展,對于單引號內的則不會。
命令替換
Linux命令的輸出可以被賦給一個變量,或者通過使用反引號引用命令,在一個字符串中使用該命令的輸出。Bash還提供了另一中新語法:無需反引號,只將命令包含在由美元符號開始的一對圓括號中即可。即:
variable_name=`COMMAND`
variable_name=$(COMMAND)
范例
數組
Bash中可以創建一維數組。數組允許將一列詞放到一個變量名中,例如一列數、一列名稱或一列文件。
數組聲明(賦值)
數組可以用內置命令decare -a來創建,或者直接給變量名一個下標來創建,如arry_name[0]=5。索引值是從0開始的整數。
如果declare命令帶-a和-r選項,將創建一個只讀數組。
也可以一次性給全部或部分元素賦值,如:
arry_name=("apple pie" banana cat )此時索引從0開始,按序關聯。
arry_name=([0]="apple pie" [2]=cat )
declare,readonly,和local內置命令也可以帶-a選項來聲明一個數組,-a選項的read命令用來從標準輸入讀入一組詞到數組元素中。
關聯數組
數組沒有上限,索引不必連續(稀疏格式)。還可以使用自定義的模式,如declare -A arry_name=([a]=apple [b]=banana),稱為關聯數組,此時必須用decare -A來聲明賦值才能用。
數組引用
要取出數組中的一個元素:${arry_name[index]}。
引用數組中的所有元素:${arry_name[*]}或${arry_name[@]}
數組切片 ${arry_name[@]:offset:num}。
eg. ${arry1[@]:1:3} 跳過第一個,取之后的3個元素。
數組長度(元素個數)
${#arry_name[*]}或${#arry_name[@]}
數組刪除
刪除整個數組:unset arry_name
刪除單個元素:unset arry_name[index]
Shell讀取用戶輸入之read
read是一個內置命令,用于從終端或文件讀取輸入。read命令讀取一個輸入行,直至遇到換行符。如果read后未跟變量,讀入的行將會賦給內置變量REPLY。也可以用read命令來中斷程序的運行,直至用戶輸入一個回車鍵。read常用方法與選項為:
read answer |
從終端讀入一行賦值給變量answer |
read first…last |
把用戶鍵入的詞依次賦給賦給變量fist…,剩余的部分全部保存到變量last中 |
read -p promt |
打印提示符,并等待用戶輸入 |
-a arryname |
讀入一組詞,以此賦值給數組arryname |
-t timeout |
等待用戶輸入時間,如果在timeout(sec)時間沒有輸入一個完整的行則退出并返回一個失敗狀態值。 |
范例
位置參量與命令行參數
用戶可以通過命令行向腳本傳遞參數,我們用位置參量(或稱為位置變量)來引用。例如:$1代表第一個位置參量。位置參量有:
$0 |
腳本名(腳本路徑) |
$1…${10} |
單獨的位置參量 |
$# |
位置參量個數 |
$* |
所有參數,全部參數合為一個字符串 |
$@ |
所有參數,全部參數分為單獨字符串;$*與$@在加雙引號時才有區別 |
帶參數的set命令將重置位置變量,且原來的參量列表就丟失了。要清除所有位置參量,可使用set –,$0始終代表腳本名。
范例:腳本如下
#!/bin/bash
echo $1
echo $2
echo $3
echo ====
for i in $*;do
echo $i
done
echo ====
for i in $@;do
echo $i
done
echo ====================
for i in "$*";do
echo $i
done
echo ====
for i in "$@";do
echo $i
done
set 1 2 3
echo $1
echo $2
echo $3
set —
echo \$1 is $1
執行結果
說明:輸入3個腳本參數"blue sky”green white給腳本test。
算術運算
Shell腳本少不了算術運算。
可以用declare -i定義一個整型變量,如果把一個整型變量賦值給一個字符串,則bash把變量值變為0。
整型變量可以進行直接進行算術運算;內置let命令也允許進行算術操作,且帶有豐富的類C操作符,雙括號(( ))是let的另一種可選形式。
范例:
說明:整型變量直接運算時,中間不能有空格,或者用雙引號括起來。
let命令是shell的內置命令,可以用來進行整數運算和數值表達式測試。
范例:
說明:let執行算術運算時,不需要用美元符號$展開變量;如果參數包含空格則需要加引號;雙括(( ))可以代替let變量,此時操作符之間可以有空格。let操作符有:
+ – * / |
加(正號)、 減(負號)、 乘、 除 |
% |
余數(取模) |
! |
邏輯非 |
~ |
按位取反 |
<< >> |
按位左移 、 按位右移 |
< > <= >= |
關系運算符,小于、 大于、 小于等于、 大于等于 |
== != |
相等 、不相等 |
& | ^ |
按位與 、按位或、按位異或 |
&& || |
邏輯與 、邏輯或 |
= *= /= %= += -= <<= >>= &= ^= |= |
賦值、快捷賦值運算符 |
算術擴展
Shell通過對一個算術表達式求值并替換結果來進行算術擴展。表達式可以像在雙引號中一樣來處理,并且可以嵌套。求值算術表達式有以下兩種格式:
$[ expression ]
$(( expression ))
范例:
因也可以把$[ ]當作算術運算的另一種方法。
當執行變量、命令、算術表達式和路徑名的擴展時,shell被設計按照指定的順序來掃描命令行。假設變量沒有被引用,處理就將按下面的順序進行。
1、花括號擴展2、代字符號擴展3、參量擴展4、變量替換5、命令替換6、算術擴展7、詞分離8、路徑名擴展
expr命令
算術運算還可以采用一種方法:expr命令。expr命令用來求表達式的值,其中便包括部分算術運算。
范例
說明:用expr時各符號間需要空格隔開,乘號需要加反斜桿轉義。
浮點運算
Bash只支持整型運算,但也可以一些其他工具如bc,awk來處理更復雜的運算。
范例
說明:用echo將表達式通過管道傳遞給bc,scale變量等于3表示小數點后面為3位小數。
條件測試
Bash可以測試兩種類型的條件:命令成功或失敗,表達式為真或為假。在任何一種類型的測試中都要使用退出狀態。退出狀態為0表示命令執行成功或表達式為真,非0表示命令執行失敗或表達式為假。狀態變量“?”中保存的是退出狀態值。
條件測試之test與let
單方括號的test命令 通常內置的test命令來測試表達式的值,test命令也可以鏈接到方括號上。這樣,既可以使用單獨的test命令,也可以通過把表達式用單括號括起來,來測試表達式的值。在用test命令或單方括號來測試表達式時,表達式中的shell元字符不會被擴展。由于要對變量進行單詞分離,因此包含空白字符的字符串必須用引號括起來。
雙方括號的test命令 用雙方括號[[ ]](內置的test復合命令)來測試表達式的值時,對變量不進行單詞分離,但可以通過元字符擴展進行模式匹配。包含空白字符的字符串必須用引號括起來。此時邏輯操作符&&(與)和||(或)代替了與test命令一起使用的-a和-o選項。
test命令操作符(除&&和||外,其他操作符兩邊及中括號兩邊都須有空格):
操作符 |
測試內容 |
字符串測試 |
|
[ string1 = string2 ] |
string1等于string2 |
[ string1 == string2 ] |
string1等于string2(可用=代替) |
[ string1 != string2 ] |
string1不等于string2 |
[ string ] |
string不為空 |
[ -z string ] |
string的長度為0 |
[ -n string ] |
string的長度不為0 |
邏輯測試 |
|
[ string1 -a string2 ] |
string1和string2都為真 |
[ string1 -o string2 ] |
string1或string2至少有一個為真 |
[ ! string ] |
字符串不匹配 |
邏輯測試(復合命令) |
|
[[ pattern1 && pattern2 ]] |
pattern1和pattern2都為真 |
[[ pattern1 || pattern2 ]] |
pattern1和pattern2至少有一個為真 |
[[!pattern ]] |
模式不匹配 |
[[ pattern1 =~ pattern2 ]] |
pattern1是否與pattern2匹配(正則表達式匹配) |
整數測試 |
|
[ int1 -eq int2 ] |
Int1等于int2 |
[ int1 -ne int2 ] |
Int1不等于int2 |
[ int1 -gt int2 ] |
Int1大于int2 |
[ int1 -ge int2 ] |
Int1大于或等于int2 |
[ int1 -lt int2 ] |
Int1小于int2 |
[ int1 -le int2 ] |
Int1小于或等于int2 |
文件測試 |
|
[ file1 -nt file2 ] |
文件file1比file2新則為真 |
[ file1 -ot file2 ] |
文件file1比file2老則為真 |
[ file1 -ef file2 ] |
文件file1和file2有相同的設備數或i節點數則為真 |
[ -e file ] |
文件是否存在(同-a);此外還有許多文件測試,如 -r/-w/x;-g/-u/-k;-t/-N/-O/-G; -b/-c/-d/-f/-h(-L)/-p/-s/-S(socket) |
范例
說明:單方括號是test的另一種表示方法,test命令不允許使用通配符,所以單方括號中[LI]不會被展開,linux與[Ll]inux不相等,返回狀態為1;當使用復合test命令時可以使用通配符,返回值為真。
說明:雙方括號的復合test,此時支持通配符,不進行變量分離;等號加波浪符(=~),則為正則表達式模式匹配。邏輯操作符用&&代替-a。
let命令和帶雙圓括號的算術表達式測試
雖然test命令可以測試算術表達式,但由于let命令豐富的類C操作符,我們更愿意使用let命令,此時將表達式放在雙圓括號來表示不同含義。
范例
說明:雙圓括號代替let命令來測試算術表達式里的值,此時括號里的變量不需要使用美元符號$展開。
條件語句
條件語句之if
條件語句能夠根據某個特定的條件是否滿足,來選擇執行相應的任務。if是最簡單的決策形式。if/else語句提供雙分支,而if/elif/else語句則提供多分支。
if語句后加一條后面跟一條或一組命令引用或測試表達式,如果該命令或測試表達式的退出狀態為0,則執行then到fi之間的語句;如果退出狀態為非0,則shell直接跳到fi后面執行命令。雙路決策則為退出狀態為0則執行then到else之間的語句;否則執行else到fi之間的語句。同理多分支則再次提供一次或多次測試。
if語句格式
詳細為:
單分支
if condition;then
COMMANS(命令組)
fi
雙分支
if condition;then
COMMANS(命令組)
else
COMMANS(命令組)
fi
多分支
if condition;then
COMMANS(命令組)
elif condition2;then
COMMANS(命令組)
…
else
COMMANS(命令組)
fi
條件conditon可為命令、test測試表達式或則雙圓括號的算術表達式(( ))。
關鍵字之間的命令采用縮進格式,是一種慣例,以使程序便于閱讀和調試。
if語句可以嵌套,如果嵌套使用if語句,fi總是與最近的if語句配套。同理最好對嵌套if語句進行縮進。
范例:
說明:反向單引號引用grep命令在/etc/passwd文件中查找$name,由于不需要看grep命令的輸出結果,所以用標準錯誤輸出和標準輸出都重定向到linux的位容器/dev/null中。
grep命令若找到了$name則退出狀態返回0,就執行then后面的語句,否則執行else后面的語句。exit 1顯示退出狀態1,說明查找失敗。
exit命令和變量"?"
exit命令用于終止腳本并返回命令行。傳遞給exit的參數是一個0~255之間的整數。如果程序返回退出狀態0,則表示程序成功退出。否則表示遇到了某種失敗。傳遞給exit的參數保存在變量"?"中。腳本程序默認的退出狀態是程序最后一條命令的執行狀態。
null命令
冒號(:)代表的null命令是shell的一個內置命令,它不做任何工作,只返回退出狀態0。null命令有時被放在if后作為一個占位符,這時if命令沒有什么事可做,卻需要有一條命令放在then后面,否則程序會產生報錯信息。null命令常常被用作循環命令的參數,作用是讓循環無限執行下去。
條件語句之case
case命令是一個多路分支命令,可用來代替if/elif命令。case變量的值與value1,value2等的值逐一比較,知道找到與之匹配的值。如果某個值與case變量匹配,程序就執行該值后面的命令,直到遇到雙分號,然后跳到esac后面繼續往下執行。
如果沒有找到與case便來那個匹配的值,程序就執行默認值"*)"后面的命令,然后跳出。case的表達式里可以用shell通配符,還可以用豎桿將兩個值相或。
格式
詳細
case 變量 in
value1)
COMMANDS
;;
value2)
COMMANDS
;;
…
*)
COMMANDS
;;
esac
范例
說明:提示用戶輸入一個選擇存入變量choice中,如果choice的值與下面的選項某項匹配則執行相應的命令。
用here文檔和case語句生成菜單
here文檔經常與case命令結合起來使用。我們可以用here文檔生成一個選項菜單顯示在屏幕上,要求用戶選擇一個菜單項,然后用case命令對照選項測試用戶的輸入以執行相應的命令。
說明:cat生成here文檔,read讀入用戶的輸入保存在choice變量中,其值與那個選項匹配便執行相應的命令。
原創文章,作者:beyond,如若轉載,請注明出處:http://www.www58058.com/40785