終于,來到了shell十三問的最后一問了... 長長吐一口氣~~~~
最后要介紹的是shell script設(shè)計中常見的循環(huán)(loop).
所謂的loop就是script中的一段在一定條件下反復(fù)執(zhí)行的代碼。
bash shell中常用的loop有如下三種:
for loop 是從一個清單列表中讀進變量的值,
并依次的循環(huán)執(zhí)行do到done之間的命令行。
例:
for var in one two three four five
do
echo -----------------
echo '$var is '$var
echo
done
上例的執(zhí)行結(jié)果將會是:
for會定義一個叫var的變量,其值依次是one two three four five。
因為有5個變量值,因此,
do與done之間的命令行會被循環(huán)執(zhí)行5次。每次循環(huán)均用
echo產(chǎn)生3個句子。而第二行中不在hard quote之內(nèi)的$var會被替換。- 當最后一個變量值處理完畢,循環(huán)結(jié)束。
我們不難看出,在for loop中,變量值的多寡,決定循環(huán)的次數(shù)。
然而,變量在循環(huán)中是否使用則不一定,得視設(shè)計需求而定。
倘若for loop沒有使用in這個keyword來制變量清單的話,其值將從
$@(或$*)中繼承:
for var; do
......
done
Tips:
若你忘記了`positional parameter, 請溫習(xí)第9章...
for loop用于處理“清單”(list)項目非常方便,
其清單除了明確指定或從postional parameter取得之外,
也可以從變量替換或者命令替換取得...
(再一次提醒:別忘了命令行的“重組”特性)
然而,對于一些“累計變化”的項目(整數(shù)的加減),for 也能處理:
for ((i = 1; i <= 10; i++))
do
echo "num is $i"
done
除了for loop, 上面的例子,
我們也可改用while loop來做到:
num=1
while [ "$num" -le 10 ]; do
echo "num is $num"
num=$(($num + 1))
done
while loop的原理與for loop稍有不同:
它不是逐次處理清單中的變量值,
而是取決于while 后面的命令行的return value:
do與done之間的命令,
然后重新判斷while后的return value。do與done之間的命令而結(jié)束循環(huán)。分析上例:
在
while之前,定義變量num=1.然后測試(
test)$num是否小于或等于10.結(jié)果為true,于是執(zhí)行
echo并將num的值加1.再作第二輪測試,此時num的值為1+1=2,依然小于或等于10,因此,為true,循環(huán)繼續(xù)。
- 直到num為10+1=11時,測試才會失敗...于是結(jié)束循環(huán)。
我們不難發(fā)現(xiàn):
若while的測試結(jié)果永遠為true的話,那循環(huán)將一直永久執(zhí)行下去:
while:; do
echo looping...
done
上面的:是bash的null command,不做任何動作,
除了返回true的return value。
因此這個循環(huán)不會結(jié)束,稱作死循環(huán)。
死循環(huán)的產(chǎn)生有可能是故意設(shè)計的(如跑daemon), 也可能是設(shè)計的錯誤。
若要結(jié)束死循環(huán),可通過signal來終止(如按下ctrl-c). (關(guān)于process與signal,等日后有機會再補充,十三問略過。)
一旦你能夠理解while loop的話,那就能理解until loop:
**與while相反, until是在return value 為false時進入循環(huán),否則,結(jié)束。
因此,前面的例子,我們也可以輕松的用until來寫:
num=1
until [ ! "$num" -le 10 ]; do
echo "num is $num"
num=$(($num + 1))
done
或者:
num=1
until [ "$num" -gt 10 ]; do
echo "num is $num"
num=$(($num + 1))
done
okay, 關(guān)于bash的三個常用的loop暫時介紹到這里。
在結(jié)束本章之前,再跟大家補充兩個loop有關(guān)的命令:
breakcontinue這兩個命令常用在復(fù)合式循環(huán)里,
也就是do ... done之間又有更進一層的loop,
當然,用在單一循環(huán)中也未嘗不可啦... ^_^
break用來中斷循環(huán),也就是強迫結(jié)束循環(huán)。
若break后面指定一個數(shù)值n的話,則從里向外中斷第n個循環(huán),
預(yù)設(shè)值為 break 1,也就是中斷當前循環(huán)。
在使用break時,需要注意的是,它與return及exit是不同的:
break是結(jié)束loop;return是結(jié)束function;exit是結(jié)束script/shell;而continue則與break相反:強迫進入下一次循環(huán)動作.
若你理解不來的話,那你可簡單的看成:
在continue在done之間的句子略過而返回到循環(huán)的頂端...
與break相同的是:continue后面也可以指定一個數(shù)值n,
以決定繼續(xù)哪一層(從里往外計算)的循環(huán),
預(yù)設(shè)值為 continue 1,也就是繼續(xù)當前的循環(huán)。
在shell script設(shè)計中,若能善用loop, 將能大幅度提高script在復(fù)雜條件下的處理能力。 請多加練習(xí)吧...
好了,該是到了結(jié)束的時候了。 婆婆媽媽地跟大家啰嗦了一堆shell的基礎(chǔ)概念。
目的不是要告訴大家“答案”,而是要帶給大家“啟發(fā)”...
在日后的關(guān)于shell的討論中,我或許經(jīng)常用"連接"的方式 指引十三問中的內(nèi)容。
以便我們在進行技術(shù)探討時,彼此能有一些討論的基礎(chǔ), 而不至于各說各話、徒費時力。
但更希望十三問能帶給你更多的思考與樂趣, 至為重要的是通過實踐來加深理解。
是的,我很重視實踐與獨立思考這兩項學(xué)習(xí)要素。
若你能夠掌握其中的真諦,那請容我說聲: 恭喜十三問你沒白看了 ^_^
p.s. 至于補充問題部分,我暫時不寫了。 而是希望:
Good luck and happy studing!
網(wǎng)中人簽名中的bash的fork bomb最后,Markdown 整理者補上本書的原作者網(wǎng)中人的個性簽名:
君子博學(xué)而日叁省乎己,則知明而行無過矣。
一個能讓系統(tǒng)shell崩潰的shell 片段:
:() { :|:& }; : # <--- 這個別亂跑!好奇會死人的!
echo '十人|日一|十十o' | sed 's/.../&\n/g' # <--- 跟你講就不聽,再跑這個就好了...
原來是一個bash的fork炸彈:ref:http://en.wikipedia.org/wiki/Fork_bomb
整理后的代碼:
:() {
:|:&
}
:
代碼分析:
(即除最后一行外)
定義了一個 shell 函數(shù),函數(shù)名是
:,而這個函數(shù)體執(zhí)行一個后臺命令
:|:即冒號命令(或函數(shù),下文會解釋)的輸出 通過管道再傳給冒號命令做輸入
最后一行執(zhí)行“:”命令
在各種shell中運行結(jié)果分析:
這個代碼只有在 bash 中執(zhí)行才會出現(xiàn)不斷創(chuàng)建進程而耗盡系統(tǒng)資源的嚴重后果;
在 ksh (Korn shell), sh (Bourne shell)中并不會出現(xiàn),
在 ksh88 和傳統(tǒng) unix Bourne shell 中冒號不能做函數(shù)名,
即便是在 unix-center freebsd 系統(tǒng)中的 sh 和 pdksh(ksh93 手邊沒有,沒試)中冒號可以做函數(shù)名,但還是不會出現(xiàn)那個效果。
原因是 sh、ksh 中內(nèi)置命令的優(yōu)先級高于函數(shù),所以執(zhí)行“:”, 總是執(zhí)行內(nèi)置命令“:”而不是剛才定義的那個恐怖函數(shù)。
但是在 bash 中就不一樣,bash 中函數(shù)的優(yōu)先級高于內(nèi)置命令, 所以執(zhí)行“:”結(jié)果會導(dǎo)致不斷的遞歸,而其中有管道操作, 這就需要創(chuàng)建兩個子進程來實現(xiàn),這樣就會不斷的創(chuàng)建進程而導(dǎo)致資源耗盡。
眾所周知,bash是一款極其強大的shell,提供了強大的交互與編程功能。
這樣的一款shell中自然不會缺少“函數(shù)”這個元素來幫助程序進行模塊化的高效開發(fā)與管理。 于是產(chǎn)生了由于其特殊的特性,bash擁有了fork炸彈。
Jaromil在2002年設(shè)計了最為精簡的一個fork炸彈的實現(xiàn)。
所謂fork炸彈是一種惡意程序,它的內(nèi)部是一個不斷在fork進程的無限循環(huán).
fork炸彈并不需要有特別的權(quán)限即可對系統(tǒng)造成破壞。
fork炸彈實質(zhì)是一個簡單的遞歸程序。
由于程序是遞歸的,如果沒有任何限制,
這會導(dǎo)致這個簡單的程序迅速耗盡系統(tǒng)里面的所有資源.