shell十三問之10:&& 與 || 差在哪?
好不容易,進入了兩位數的章節了... 一路走來,很辛苦吧?也很快樂吧? ^_^
在解答本章題目之前,先讓我們瞭解一個概念: return value。
我們在shell下跑的每一個command或function,
在結束的時候都會傳回父進程一個值,稱為 return value
。
在shell command line中可用$?
,
這個變量得到最"新"的一個return value
,
也就是剛剛結束的那個進程傳回的值。
Return Value
(RV)的取值為0-255之間,
由進程或者script的作者自行定義:
若在script裡,用exit RV 來指定其值; 若沒有指定, 在結束時,以最後一個命令的RV,為script的RV值。
若在function裡,則用return RV 來代替exit RV即可。
Return Value
的作用:用來判斷進程的退出狀態(exit status).
進程的退出狀態有兩種:
- 0值為"真"(true)
- 非0值為"假"(false)
舉個例子來說明好了: 假設當前目錄內有一個my.file的文件, 而no.file是不存在的:
$ touch my.file
$ ls my.file
$ echo $? #first echo
0
$ ls no.file
ls: no.file: No such file or directory
$ echo $? #second echo
1
$ echo $? #third echo
0
上例的:
- 第一個echo是關於
ls my.file
的RV,可得到0的值,因此為true。 - 第二個echo是關於
ls no.file
的RV,得到非0的值,因此為false。 - 第三個echo是關於
echo $?
的RV,得到0值, 因此為true。
請記住:
每一個command在結束時,都會返回return value
,不管你跑什麼命令...
然而,有一個命令卻是“專門”用來測試某一條而返回return value
,
以供true或false的判斷, 它就是test
命令。
若你用的是bash, 請在command line下,
打man test
,或者 man bash
來瞭解這個test
的用法。
這是你可用作參考的最精準的文件了,要是聽別人說的,僅作參考就好...
下面,我只簡單作一些輔助說明,其餘的一律以 man
為準:
首先,test
的表達式,我們稱為expression,其命令格式有兩種:
test expression
或者
[ expression ]
Note:
請務必注意
[]
之間的空白鍵!
用哪一種格式無所謂,都是一樣的效果。 (我個人比較喜歡後者...)
其次,bash的test
目前支持的測試對象只有三種:
- string:字符串,也就是純文字。
- integer:整數(0或正整數、不含負數或小數)
- file: 文件
請初學者,一定要搞清楚這三者的差異,
因為test
所使用的expression是不一樣的。
以A=123這個變量為例:
[ "$A" = 123 ]
#是字符串測試,測試$A是不是1、2、3這三個字符。[ "$A" -eq 123 ]
#是整數測試,以測試$A是否等於123.[-e "$A" ]
#文件測試,測試123這份文件是否存在.
第三,
當expression測試為“真”時, test
就返回0(true)的return value
;
否則,返回非0(false).
若在 expression 之前加一個!
(感嘆號),則在expression為假時,return value為0,
否則, return value 為非0值。
同時,test
也允許多重複合測試:
- expression1 -a expression2 #當兩個expression都為true,返回0,否則,返回非0;
- expression1 -o expression2 #當兩個expression均為false時,返回非0,否則,返回0;
例如:
[ -d "$file" -a -x "$file" ]
表示當$file是一個目錄,且同時具有x權限時,test才會為true。
第四,在command line中使用test
時,請別忘記命令行的“重組”特性,
也就是在碰到meta時,會先處理meta,在重新組建命令行。
(這個概念在第2章和第4章進行了反覆強調)
比方說, 若test
碰到變量或者命令替換時,
若不能滿足 expression的格式時,將會得到語法錯誤的結果。
舉例來說好了:
關於[ string1 = string2 ]
這個test格式,
在等號兩邊必須要有字符串,其中包括空串(null串,可用soft quote或者hard quote取得)。
假如$A目前沒有定義,或被定義為空字符串的話, 那如下的用法將會失敗:
$ unset A
$ [ $A = abc ]
[: =: unary oprator expected
這是因為命令行碰到$這個meta時,會替換$A的值,
然後,再重組命令行,那就變成了:
[ = abc ]
,
如此一來,=的左邊就沒有字符串存在了,
因此,造成test的語法錯誤。
但是,下面這個寫法則是成立的。
$ [ "$A" = abc ]
$ echo $?
1
這是因為命令行重組後的結果為:
[ "" = abc ]
,
由於等號的左邊我們用soft quote得到一個空串,
而讓test的語法得以通過...
讀者諸君,請務必留意這些細節哦,
因為稍一不慎,將會導致test
的結果變了個樣。
若您對test
還不是很有經驗的話,
那在使用test時,不妨先採用如下這一個"法則":
若在test
中碰到變量替換,用soft quote是最保險的*。
若你對quoting不熟的話,請重新溫習第四章的內容吧...^_^
okay, 關於更多的test
的用法,老話一句:請看其man page (man test
)吧!^_^
雖然洋洋灑灑讀了一大堆,或許你還在嘀咕...那...那個return value
有啥用?
問得好: 告訴你:return value的作用可大了, 若你想要你的shell變"聰明"的話,就全靠它了: 有了return value, 我們可以讓shell根據不同的狀態做不同的事情...
這時候,才讓我來揭曉本章的答案吧~~~~^_^
&&
與 ||
都是用來"組建" 多個command line用的;
command1 && command2
# command2只有在command1的RV為0(true)的條件下執行。command1 || command2
# command2 只有在command1的RV為非0(false)的條件下執行。
以例子來說好了:
$ A=123
$ [ -n "$A" ] && echo "yes! it's true."
yes! it's true.
$ unset A
$ [ -n "$A" ] && echo "yes! it's true."
$ [ -n "$A" ] || echo "no, it's Not true."
no, it's Not true
Note:
[ -n string ]
是測試string長度大於0, 則為true。
上例中,第一個&&
命令之所以會執行其右邊的echo
命令,
是因為上一個test
返回了0的RV值;
但第二個,就不會執行,因為test
返回了非0的結果...
同理,||
右邊的echo
會被執行,卻正是因為左邊的test
返回非0所引起的。
事實上,我們在同一個命令行中,可用多個&&
或 ||
來組建呢。
$ A=123
$ [ -n "$A" ] && echo "yes! it's true." || echo "no, it's Not ture."
yes! it's true.
$ unset A
$ [ -n "$A" ] && echo "yes! it's true." || echo "no, it's Not ture."
no, it's Not true
怎樣,從這一刻開始,你是否覺得我們的shell是“很聰明”的呢? ^_^
好了,最後佈置一道練習題給大家做做看: 下面的判斷是:當$A被賦值時,在看看其是否小於100,否則輸出too big!
$ A=123
$ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
$ too big!
若我取消A,照理說,應該不會輸出文字啊,(因為第一個條件不成立)。
$ unset A
$ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
$ too big!
為何上面的結果也可得到呢? 又如何解決呢?
Tips:
修改的方法有很多種, 其中一種方法可以利用第7章中介紹過
command group
...
快告訴我答案,其餘免談....
解決方法1:sub-shell
:
$ unset A
$ [ -n "$A" ] && ( [ "$A" -lt 100 ] || echo 'too big!' )
解決方法二:command group
:
$ unset A
$ [ -n "$A" ] && { [ "$A" -lt 100 ] || echo 'too big!'}