$ if true;then echo YES; else echo NO; fiYES$ if false;then echo YES; else echo NO; fiNO
$ if true && true;then echo YES; else echo NO; fiYES$ if true && false;then echo YES; else echo NO; fiNO$ if false && false;then echo YES; else echo NO fiNO$ if fse & fiNO$ if fse &ES; ;then echo YES; else echo NO; fiNO
$ if true || true;then echo YES; else echo NO; fiYES$ if true || false;then echo YES; else echo NO; fiYES$ if false || true;then echo YES; else echo NO; fiYES$ if false || false;then echo YES; else echo NO; fiNO
$ if ! false;then echo YES; else echo NO; fiYES$ if ! true;then echo YES; else echo NO; fiNO
可以看出
true和
false按照我們對邏輯運算的理解進行著,但為了能夠更好的理解Shell 對邏輯運算的實現,我們還得弄清楚,
true和
false是怎麼工作的?
答案是:否。
true和
false它們本身並非邏輯值,它們都是Shell 的內建指令,只是它們的回傳值是一個「邏輯值」:
$ true$ echo $?0$ false$ echo $?1
可以看到
true返回了0,而
false則回傳了1 。跟我們離散數學裡學的真值1 和0 並不是對應的,而且相反的。
$ help true falsetrue: true Return a successful result.false: false Return an unsuccessful result.$ type true falsetrue is a shell builtinfalse is a shell builtin
說明:
$?是一個特殊變量,存放有上一次進程的結束狀態(退出狀態碼)。
從上面的操作不難聯想到在C 語言程式設計中為什麼會強調在
main函數前面加上
int,並在最後加上
return 0。因為在Shell 裡,將把0 當作程式是否成功結束的標誌,這就是Shell 裡頭
true和
false的實質,它們用以反應某個程序是否正確結束,而並非傳統的真假值(1 和0),相反地,它們返回的是0 和1 。不過慶幸地是,我們在做邏輯運算時,無須關心這些。
從上節中,我們已經清楚地了解了Shell 下的「邏輯值」是什麼:是進程退出時的返回值,如果成功返回,則為真,如果不成功返回,則為假。
而條件測試正好使用了
test這麼一個指令,它用來進行數值測試(各種數值屬性測試)、字串測試(各種字串屬性測試)、文件測試(各種文件屬性測試),我們透過判斷對應的測試是否成功,從而完成各種常規工作,再加上各種測試的邏輯組合後,將可以完成更複雜的工作。
$ if test 5 -eq 5;then echo YES; else echo NO; fiYES$ if test 5 -ne 5;then echo YES; else echo NO; fiNO
$ if test -n not empty;then echo YES; else echo NO; fiYES$ if test -z not empty;then echo YES; else echo NO; fiNO$ if test -z ;then echo YES; else echo NO; fiYES$; if test -n ;then echo YES; else echo NO; fiNO
$ if test -f /boot/System.map; then echo YES; else echo NO; fiYES$ if test -d /boot/System.map; then echo YES; else echo NO; fiNO
$ a=5;b=4;c=6;$ if test $a -eq 5 -a $b -eq 4 -a $c -eq 6; then echo YES; else echo NO; fiYES
$ if test -f /etc/profile -o -d /etc/profile;then echo YES; else echo NO; fiYES
!非運算
$ if test ! -f /etc/profile; then echo YES; else echo NO; fiNO
上面僅僅演示了
test命令一些非常簡單的測試,你可以通過
help test獲取
test的更多用法。需要注意的是,
test指令內部的邏輯運算和Shell 的邏輯運算子有一些區別,對應的為
-a和
&&,
-o與
||,這兩者不能混淆使用。而非運算都是
!,下面將它們進行比較。
$ cat > test.sh#!/bin/bashecho test[CTRL+D] # 按下組合鍵CTRL與D結束cat輸入,後同,不再註明$ chmod +x test.sh$ if test -s test .sh -a -x test.sh; then echo YES; else echo NO; fiYES$ if test -s test.sh && test -x test.sh; then echo YES; else echo NO; fiYES
$ str1=test$ str2=test$ if test -z $str2 -o $str2 == $str1; then echo YES; else echo NO; fiYES$ if test -z $str2 || test $str2 == $str1; then echo YES; else echo NO; fiYES
$ i=5$ if test ! $i -lt 5 -a $i -ne 6; then echo YES; else echo NO; fiYES$ if ! test $i -lt 5 -a $i -eq 6; then echo YES ; else echo NO; fiYES
很容易找出它們的區別,
-a和
-o作為測試命令的參數用在測試命令的內部,而
&&和
||則用來運算測試的回傳值,
!為兩者通用。需要關注的是:
有時可以不用
!運算符,例如
-eq和
-ne剛好相反,可用來測試兩個數值是否相等;
-z與
-n也是對應的,用來測試某個字串是否為空
在
Bash裡,
test指令可以用[] 運算子取代,但需要注意,[
之後與] 之前需要再增加額外的空格
在測試字串時,所有變數建議用雙引號包含起來,以防止變數內容為空時出現僅有測試參數,沒有測試內容的情況
下面我們用實例來示範上面三個注意事項:
-ne和
-eq對應的,我們有時候可以免去
!運算
$ i=5$ if test $i -eq 5; then echo YES; else echo NO; fiYES$ if test $i -ne 5; then echo YES; else echo NO; fiNO$ if test ! $i -eq 5; then echo YES; else echo NO; fiNO
用
[ ]可以取代
test,這樣看上去會「美觀」很多
$ if [ $i -eq 5 ]; then echo YES; else echo NO; fiYES$ if [ $i -gt 4 ] && [ $i -lt 6 ]; then echo YES; else echo NO; fiYES
記得給一些字串變數加上
,記得[之後與
]之前多加一個空格
$ str=$ if [ $str = test]; then echo YES; else echo NO; fi-bash: [: missing `]'NO$ if [ $str = test ]; then echo YES; else echo NO; fi- bash: [: =: unary operator expectedNO$ if [ $str = test ]; then echo YES; else echo NO; fiNO
到這裡,條件測試就介紹完了,下面介紹命令列表,實際上在上面我們已經使用過了,即多個test命令的組合,通過
&&,
||和
!組合起來的命令序列。這種命令序列可以有效替換
if/then的條件分支結構。這不難想到我們在C 語言程式設計中經常做的如下的選擇題(很無聊的例子,但是有意義):下面是否會列印
j,如果列印,將列印什麼?
#include <stdio.h>int main(){ int i, j; i=5;j=1; if ((i==5) && (j=5)) printf(%dn, j); return 0;}
很容易知道將列印數字5,因為
i==5這個條件成立,而且接著是
&&,要判斷整個條件是否成立,我們得進行後面的判斷,可是這個判斷並非常規的判斷,而是先把
j修改為5,再轉換為真值,所以條件為真,印出5 。因此,這句可以解釋為:如果
i等於5,那麼把
j賦值為5,如果
j大於1 (因為之前已經為真),那麼印出
j的值。這樣用
&&連結起來的判斷語句取代了兩個
if條件分支語句。
正是基於邏輯運算特有的性質,我們可以透過
&&,
||來取代
if/then等條件分支結構,這樣就產生了指令列表。
指令清單的執行規律符合邏輯運算的運算規律,用
&&連接起來的命令,如果前者成功返回,將執行後面的命令,反之不然;用
||連接起來的命令,如果前者成功返回,將不執行後續命令,反之不然。
$ ping -c 1 www.lzu.edu.cn -W 1 && echo =======connected=======
非常有趣的問題出來了,也就是我們上面已經提到的:為什麼要讓C 程式在
main()函數的最後返回0 ?如果不這樣,把這種程式放入指令清單會有什麼樣的結果?你自己寫個簡單的C 程序,然後放入指令列表看看。
有時會用命令列表取代
if/then等條件分支結構可以省掉一些程式碼,而且使得程式比較美觀、易讀,例如:
#!/bin/bashecho $#echo $1if [ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null);then echo YESfi
說明:上例要求參數個數為1 且類型為數字。
再加上
exit 1,我們將省掉
if/then結構
#!/bin/bashecho $#echo $1! ([ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null)) && exit 1echo YES
這樣處理後,程式參數的判斷只需要簡單的一行程式碼,而且變得更美觀。
這一節介紹了Shell 程式設計中的邏輯運算,條件測試和命令清單。