《LINUX學習:shell的編程結構體(函數、條件結構、循環結構)》要點:
本文介紹了LINUX學習:shell的編程結構體(函數、條件結構、循環結構),希望對您有用。如果有疑問,可以聯系我們。
本文目次:
1.1 shell函數
1.2 條件布局:if
1.3 條件布局:case
1.4 條件布局:select
1.5 循環布局:for
1.6 循環布局:while
1.7 循環布局:until
1.8 exit、break、continue和return
?
在shell中,函數可以被當作命令一樣執行,它是命令的組合布局體.可以將函數看成是一個普通命令或者一個小型腳本.
起首給出幾個關于函數的結論:
(1).當在bash中直接挪用函數時,如果函數名和命令名相同,則優先執行函數,除非使用command命令.例如:定義了一個名為rm的函數,在bash中輸入rm執行時,執行的是rm函數,而非/bin/rm命令,除非使用"command rm ARGS".
(2).當前shell定義的函數只能在當前shell使用,子shell無法承繼父shell的函數定義.除非使用"export -f"將函數導出為全局函數.
(2).界說了函數后,可以使用unset -f移除當前shell中已界說的函數.
(3).除非出現語法錯誤,或者已經存在一個同名只讀函數,不然函數的退出狀態碼是函數內部結構中最后執行的一個命令的退出狀態碼.
(4).可以使用typeset -f [func_name]或declare -f [func_name]查看當前shell已界說的函數名和對應的界說語句.使用typeset -F或declare -F則只顯示當前shell中已界說的函數名.
(5).函數可以遞歸,遞歸條理可以無限.
函數的語法布局:
[ function ] name () compound-cmd [redirection]
上面的語法結構中定義了一個名為name的函數,關鍵字function是可選的,如果使用了function關鍵字,則name后的括號可以省略.compound-cmd是函數體,通常使用大括號{}包抄,由于歷史原因,大括號本身也是關鍵字,所以為了不產生歧義,函數體必須和大括號使用空格、制表符、換行符分隔開來.還可以指定可選的函數重定向功能,這樣當函數被調用的時候,指定的重定向也會被執行.
例如:界說一個名為rm的函數,該函數會將傳遞的所有文件移動到"~/backup"目錄下,目的是替代rm命令,避免誤刪除的危險操作.
[root@linuxidc ~]# function rm () { [ -d ~/rmbackup ] || mkdir ~/rmbackup;/bin/mv -f $@ ~/rmbackup; } &>/dev/null
在挪用rm函數時,只需是給rm函數傳遞參數即可.例如,要刪除/tmp/a.log.
[root@linuxidc ~]# rm /tmp/a.log
在執行函數時,會將執行可能輸出的信息重定向到/dev/null中.
為了讓函數在子shell(例如腳本)中也可以使用,使用export的"-f"選項將其導出為全局函數.撤消函數的導出則使用export的"-n"選項.
export -f rm export -n rm
關于shell函數,還有幾個必要說明的知識點:
(6).shell函數也接受位置變量$0、$1、$2...,但函數的位置參數是調用函數時傳遞給函數的,而非傳遞給腳本的參數.所以腳本的位置變量和函數的位置變量是不同的,但是$0和腳本的位置變量$0是一致的.另外,函數也接受特殊變量"$#",和腳本的"$#"一樣,它也表現位置變量的個數.
(7).函數體內部可以使用return命令,當函數結構體中執行到return命令時將退出整個函數.return后可以帶一個狀態碼整數,即return n,表現函數的退出狀態碼,不給定狀態碼時默認狀態碼為0.
(8).函數結構體中可以使用local命令定義本地變量,例如:local i=3.本地變量只在函數內部(包含子函數)可見,函數外不可見.
?
語法布局:
if test-commands1; then
? ? commands1;
[elif test-commands2; then
? ? commands2;]
...
[else
? ? commands3;]
fi
if的判斷很簡單,一切都以返回狀態碼是否為0為判決條件.如果test-commands1執行后的退出狀態碼為0(不是其執行結果為0),則執行commands1部分的結構體,不然如果test-commands2返回0則執行commands2部分的結構體,如果都不滿足,則執行commands3的結構體.
常見的test-commands有幾種類型:
(1).一條普通的敕令.只要該敕令退出狀態碼為0,則執行then后的語句體.例如:
if echo haha &>/dev/null;then echo go;fi
(2).測試語句.例如test、[]、[[]].
(3).使用邏輯運算符,包含!、&&和||.該特性主要是為普通命令而提供,因為測試語句自身就支持邏輯運算.所以,對于測試語句就提供了兩種寫法,一種是將邏輯運算符作為測試語句的一部分,一種是將邏輯運算符作為if語句的一部分.例如:
if ! id "$name" &>/dev/null;then echo "$name" miss;fi if ! [ 3 -eq 3 ];then echo go;fi if [ ! 3 -eq 3 ];then echo go;fi if [ 3 -eq 3 ] && [ 4 -eq 4 ] ;then echo go;fi if [ 3 -eq 3 -a 4 -eq 4 ];then echo go;fi if [[ 3 -eq 3 && 4 -eq 4 ]];then echo go;fi
注意,在if語句中使用()不能改變優先級,而是讓括號內的語句成為命令列表并進入子shell運行.因此,要改變優先級時,必要在測試語句中完成.
?
語法布局:
case word in
? ? [ [(] pattern [| pattern]…)
? ? ? ?command-list ;;]
? ? …
esac
sysV作風的服務啟動腳本是shell腳本中使用case語句最典型案例.例如:
case "$1" in start) start;; stop) stop;; restart) restart;; reload | force-reload) reload;; status) status;; *) echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}" exit 2 esac
從上面的示例中,可以看出一些結論:
(1).case中的每個小分句都以雙分號";;"結尾,但最后一個小分句的雙分號可以省略.實際上,小分句除了使用";;"結尾,還可以使用";&"和";;&"結尾,只不外意義不同,它們用的不多,不外為了文章完整性,稍后還是給出說明.
(2).每個小分句中的pattern部分都使用括號"()"包抄,只不過左括號"("不是必須的.
(3).每個小分句的pattern支持通配符模式匹配(不是正則匹配模式,因此只有3種通配元字符:"*"、"?"和[...]),其中使用"|"分隔多個通配符pattern,表現滿足其中一個pattern即可.例如"([yY] | [yY][eE][sS]])"表現即可以輸入單個字母的y或Y,還可以輸入yes三個字母的任意大小寫格式.
set -- y;case "$1" in ([yY]|[yY][eE][sS]) echo right;;(*) echo wrong;;esac
其中"set -- string_list"的作用是將輸入的string_list依照IFS分隔后分別賦值給位置變量$1、$2、$3...,因此此處是為$1賦值字符"y".
(4).最后一個小分句使用的pattern是"*",表示無法匹配前面所有小分句時,將匹配該小分句.一般最后一個小分句都會使用"*"避免case語句無法匹配的情況,在shell腳本中,此小分句一般用于提示用戶腳本的使用辦法,即給出腳本的Usage.
(5).附加一個結論:如果任何模式都不匹配,該命令的返回狀態是零;不然,返回最后一個被執行的命令的返回值.
如果小分句不是使用雙分號";;"結尾,而是使用";&"或";;&"結尾,則case語句的行為將轉變.
◇ ";;"結尾符號表現小分句執行完成后立即退出case語句.
◇ ";&"表現繼續執行下一個小分句中的command部分,而無需進行匹配動作,并由此小分句的結尾符號來決定是否繼續操作下一個小分句.
◇ ";;&"表現繼續向后(不止是下一個,而是一直向后)匹配小分句,如果匹配成功,則執行對應小分句中的command部分,并由此小分句的結尾符號來決定是否繼續向后匹配.
示例如下:
set -- y case "$1" in ([yY]|[yY][eE][sS]) echo yes;& ([nN]|[nN][oO]) echo no;; (*) echo wrong;; esac yes no
在此示例中,$1能匹配第一個小分句,但第一個小分句的結尾符號為";&",所以無需判斷地直接執行第二個小分句的"echo no",但第二個小分句的結尾符號為";;",于是直接退出case語句.因此,即使$1無法匹配第二個小分句,case語句的成果中也輸出了"yes"和"no".
set -- y case "$1" in ([yY]|[yY][eE][sS]) echo yes;;& ([nN]|[nN][oO]) echo no;; (*) echo wrong;; esac yes wrong
在此示例中,$1能匹配第一個小分句,但第一個小分句的結尾符號為";;&",所以繼續向下匹配,第二個小分句未匹配勝利,直到第三個小分句才被匹配上,于是執行第三個小分句中的"echo wrong",但第三個小分句的結尾符號為";;",于是直接退出case語句.所以,結果中輸出了"yes"和"wrong".
?
shell中提供菜單選擇的條件判斷布局.例如:
[root@linuxidc ~]# select fname in cat dog sheep mouse;do echo your choice: \"$REPLY\) $fname\";break;done 1) cat 2) dog 3) sheep 4) mouse #? 3 # 在此選擇序號3 your choice: "3) sheep" # 將輸出序號3對應的內容
語法布局:
select name [ in word ] ; do cmd_list ; done
它的布局幾乎和for循環的布局相同.有以下幾個要點:
(1).in關鍵詞后的word將根據IFS變量進行分割,分割后的每一項都進行編號,作為菜單序號被輸出,如果省略in word,則等價于"in $@",即將地位變量的內容作為菜單項.
(2).當選擇菜單序號后,該序號的內容將保留到變量name中,并且所輸入的內容(一般是序號值,例如上面的例子中輸入的3,但不規定一定要輸入序號值,例如隨便輸入幾個字符)保留保留到特殊變量REPLY中.
(3).每次輸入選擇后,select語句都將重置,如果輸入的菜單序號存在,則cmd_list會重新執行,變量name也將重置.如果沒有break敕令,則select語句會一直運行,如果遇到break敕令,將退出select語句.
仍舊是上面的示例:但不是用break
[root@linuxidc ~]# select fname in cat dog sheep mouse;do echo your choice: \"$REPLY\) $fname\";done 1) cat 2) dog 3) sheep 4) mouse #? 2 your choice: "2) dog" #? habagou # 隨意輸入幾個字符 your choice: "habagou) " # 變量fname被重置為空,變量REPLY被賦予了輸入的值habagou #? 2 3 your choice: "2 3) " #? ^C # 直到殺失落進程select才結束
?
for循環再shell腳本中應用極其廣泛,它有兩種語法布局:
布局一:for name [ [ in [ word ... ] ] ; ] do cmd_list ; done
布局二:for (( expr1 ; expr2 ; expr3 )) ; do cmd_list ; done
結構一中:將擴展in word,然后依照IFS變量對word進行分割,并依次將分割的單詞賦值給變量name,每賦值一次,執行一次循環體cmd_list,然后再繼續將下一個單詞賦值給變量name,直到所有變量賦值結束.如果省略in word,則等價于"in $@",即展開位置變量并依次賦值給變量name.注意,如果word中使用引號包圍了某些單詞,這引號包圍的內容被分割為一個單詞.
例如:
[root@linuxidc ~]# for i in 1 2 3 4;do echo $i;done 1 2 3 4
[root@linuxidc ~]# for i in 1 2 "3 4";do echo $i;done 1 2 3 4
結構二中:該結構的expr部分只支持數學計算和比擬.首先計算expr1,再判斷expr2的返回狀態碼,如果為0,則執行cmd_list,并將計算expr3的值,并再次判斷expr2的狀態碼.直到expr2的返回狀態碼不為0,循環結束.
例如:
[root@linuxidc ~]# for ((i=1;i<=3;++i));do echo $i;done 1 2 3
[root@linuxidc ~]# for ((i=1,j=3;i<=3 && j>=2;++i,--j));do echo $i $j;done 1 3 2 2
?
使用while循環盡量要讓條件運行到可以退出循環,不然無限循環.一般都在命令體部分加上變量的改變行為.
語法布局:
while test_cmd_list; do cmd_list; done
首先執行test_cmd_list中的命令,當test_cmd_list的最后一個命令的狀態碼為0時,將執行一次cmd_list,然后回到循環的開首繼續執行test_cmd_list.只有test_cmd_list中最后一個測試命令的狀態碼非0時,循環才會退出.
例如:計算1到10的算術和.
[root@linuxidc ~]# let i=1,sum=0;while [ $i -le 10 ];do let sum=sum+i;let ++i;done;echo $sum 55
在此例中,test_cmd_list中只有一個命令[ $i -le 10 ],所以它的狀態直接決議整個循環何時退出.
test_cmd_list中可以是多個命令,但千萬要考慮清楚,是否要讓決定退出循環的測試命令處在列表的尾部,不然將進入無線循環.
[root@linuxidc ~]# let i=1,sum=0;while echo $i;[ $i -le 10 ];do let sum=sum+i;let ++i;done;echo $sum 1 2 3 4 5 6 7 8 9 10 11 55
對付while循環,有另外兩種常見的寫法:
(1).test_cmd_list部門使用一個冒號":"或者true命令,使得while進入無限循環.
while :;do? ? ? ? # 或者"while true;do"
? ? ...
done
(2).使用read命令從標準輸入中按行讀取值,然后保留到變量line中(既然是read命令,所以可以保留到多個變量中),讀取一行是一個循環.
由于標準輸入既可以來源于重定向,也可以來源于管道(本色還是重定向),所以有幾種常見的寫法:
寫法一:使用管道通報內容,這是最爛的寫法
echo "abc xyz" | while read field1 field2? ? # 按IFS朋分,并賦給兩個變量
do?
? ? ...
done
寫法二:
while read line
do
? ? ...
done <<< "abc xyz"
寫法三:從文件中讀取內容
while read line
do
? ? ...
done </path/filename
既然是讀取尺度輸入,于是還可以衍生出幾種寫法:
辦法四:while read var;do ...;done < <(cmd_list)? ? ? ? ? # 采用進程替換
辦法五:exec <filename;while read var;do ...;done? ? ? ? ?# 改變標準輸入
盡管寫法有多種,但注意,它們并不等價.辦法一中使用的是管道符號,這使得while語句在子shell中執行,這意味著while語句內部設置的變量、數組、函數等在循環外部都不再生效.例如:
#!/bin/bash echo "abc xyz" | while read line do new_var=$line done echo the variable new_var is null: $new_var?
該腳本的執行成果中,$new_var的值將為空.
使用除寫法一外的任意一種寫法,在while循環外部都能繼續獲得while內的環境.例如,使用寫法二的here string取代寫法一:
#!/bin/bash while read line do new_var=$line done <<< "abc xyz" echo the variable new_var is null: $new_var?
由此可以發現,在上面的5種寫法中,年夜眾使用的最廣泛寫法一其實是最爛的一種,如果沒注意寫法一中while是在子shell運行,很可能會一直疑惑,為什么在while循環里設置好的變量或數組在循環一結束就成了空值呢.
?
until和while循環基本一致,所分歧的僅僅只是test_cmd_list的意義.
語法布局:
until test_cmd_list; do cmd_list; done
首先斷定test_cmd_list中的最后一個命令,如果狀態碼為非0,則執行一次cmd_list,然后再返回循環的開頭再次執行test_cmd_list,直到test_cmd_list的最后一個命令狀態碼為0時,才退出循環.
和while不同的是,當判斷test_cmd_list最后一個命令的狀態滿足退出條件時直接退出循環,也便是說循環是在test_cmd_list最后一個命令處退出的.
例如:
[root@linuxidc ~]# i=5;until echo haha;[ "$i" -eq 0 ];do let --i;echo $i;done haha 4 haha 3 haha 2 haha 1 haha 0 haha
?
exit [n] ? ? ? ? :退出當前shell,在腳本中應用則表現退出整個腳本(子shell).其中數值n表現退出狀態碼.
break [n] ? ? :退出整個循環,包含for、while、until和select語句.其中數值n表示退出的循環層次.
continue [n] :退出當前循環進入下一次循環.n表現繼續執行第n次循環.
return [n] ? ? :退出整個函數.n表現函數的退出狀態碼.
本文永遠更新鏈接地址:
《LINUX學習:shell的編程結構體(函數、條件結構、循環結構)》是否對您有啟發,歡迎查看更多與《LINUX學習:shell的編程結構體(函數、條件結構、循環結構)》相關教程,學精學透。維易PHP學院為您提供精彩教程。
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/8868.html