Shell 布爾運算

2018-10-26 16:48 更新

前言

上個禮拜介紹了Shell編程范例之?dāng)?shù)值運算,對 Shell 下基本數(shù)值運算方法做了簡單的介紹,這周將一起探討布爾運算,即如何操作“真假值”。

在 Bash 里有這樣的常量(實際上是兩個內(nèi)置命令,在這里我們姑且這么認為,后面將介紹),即 true 和 false,一個表示真,一個表示假。對它們可以進行與、或、非運算等常規(guī)的邏輯運算,在這一節(jié),我們除了討論這些基本邏輯運算外,還將討論Shell編程中的條件測試命令列表,并介紹它們和布爾運算的關(guān)系。

常規(guī)的布爾運算

這里主要介紹 Bash 里頭常規(guī)的邏輯運算,與、或、非。

在 Shell 下如何進行邏輯運算

范例:true or false

單獨測試 truefalse,可以看出 true 是真值,false 為假

$ if true;then echo "YES"; else echo "NO"; fi
YES
$ if false;then echo "YES"; else echo "NO"; fi
NO

范例:與運算

$ if true && true;then echo "YES"; else echo "NO"; fi
YES
$ if true && false;then echo "YES"; else echo "NO"; fi
NO
$ if false && false;then echo "YES"; else echo "NO"; fi
NO
$ if false && true;then echo "YES"; else echo "NO"; fi
NO

范例:或運算

$ if true || true;then echo "YES"; else echo "NO"; fi
YES
$ if true || false;then echo "YES"; else echo "NO"; fi
YES
$ if false || true;then echo "YES"; else echo "NO"; fi
YES
$ if false || false;then echo "YES"; else echo "NO"; fi
NO

范例:非運算,即取反

$ if ! false;then echo "YES"; else echo "NO"; fi
YES
$ if ! true;then echo "YES"; else echo "NO"; fi
NO

可以看出 truefalse 按照我們對邏輯運算的理解進行著,但是為了能夠更好的理解 Shell 對邏輯運算的實現(xiàn),我們還得弄清楚,truefalse 是怎么工作的?

Bash 里頭的 true 和 false 是我們通常認為的 1 和 0 么?

回答是:否。

范例:返回值 v.s. 邏輯值

truefalse 它們本身并非邏輯值,它們都是 Shell 的內(nèi)置命令,只是它們的返回值是一個“邏輯值”:

$ true
$ echo $?
0
$ false
$ echo $?
1

可以看到 true 返回了 0,而 false 則返回了 1 。跟我們離散數(shù)學(xué)里學(xué)的真值 1 和 0 并不是對應(yīng)的,而且相反的。

范例:查看 true 和 false 幫助和類型

$ help true false
true: true
     Return a successful result.
false: false
     Return an unsuccessful result.
$ type true false
true is a shell builtin
false is a shell builtin

說明:$? 是一個特殊變量,存放有上一次進程的結(jié)束狀態(tài)(退出狀態(tài)碼)。

從上面的操作不難聯(lián)想到在 C 語言程序設(shè)計中為什么會強調(diào)在 main 函數(shù)前面加上 int,并在末尾加上 return 0 。因為在 Shell 里,將把 0 作為程序是否成功結(jié)束的標(biāo)志,這就是 Shell 里頭 truefalse 的實質(zhì),它們用以反應(yīng)某個程序是否正確結(jié)束,而并非傳統(tǒng)的真假值(1 和 0),相反地,它們返回的是 0 和 1 。不過慶幸地是,我們在做邏輯運算時,無須關(guān)心這些。

條件測試

從上節(jié)中,我們已經(jīng)清楚地了解了 Shell 下的“邏輯值”是什么:是進程退出時的返回值,如果成功返回,則為真,如果不成功返回,則為假。

而條件測試正好使用了 test 這么一個指令,它用來進行數(shù)值測試(各種數(shù)值屬性測試)、字符串測試(各種字符串屬性測試)、文件測試(各種文件屬性測試),我們通過判斷對應(yīng)的測試是否成功,從而完成各種常規(guī)工作,再加上各種測試的邏輯組合后,將可以完成更復(fù)雜的工作。

條件測試基本使用

范例:數(shù)值測試

$ if test 5 -eq 5;then echo "YES"; else echo "NO"; fi
YES
$ if test 5 -ne 5;then echo "YES"; else echo "NO"; fi
NO

范例:字符串測試

$ if test -n "not empty";then echo "YES"; else echo "NO"; fi
YES
$ if test -z "not empty";then echo "YES"; else echo "NO"; fi
NO
$ if test -z "";then echo "YES"; else echo "NO"; fi
YES
$ if test -n "";then echo "YES"; else echo "NO"; fi
NO

范例:文件測試

$ if test -f /boot/System.map; then echo "YES"; else echo "NO"; fi
YES
$ if test -d /boot/System.map; then echo "YES"; else echo "NO"; fi
NO

各種邏輯測試的組合

范例:如果 a,b,c 都等于下面對應(yīng)的值,那么打印 YES,通過 -a 進行"與"測試

$ 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"; fi
YES

范例:測試某個“東西”是文件或者目錄,通過 -o 進行“或”運算

$ if test -f /etc/profile -o -d /etc/profile;then echo "YES"; else echo "NO"; fi
YES

范例:測試某個“東西”是否為文件,測試 ! 非運算

$ if test ! -f /etc/profile; then echo "YES"; else echo "NO"; fi
NO

上面僅僅演示了 test 命令一些非常簡單的測試,你可以通過 help test 獲取 test 的更多用法。需要注意的是,test 命令內(nèi)部的邏輯運算和 Shell 的邏輯運算符有一些區(qū)別,對應(yīng)的為 -a&&-o||,這兩者不能混淆使用。而非運算都是 !,下面對它們進行比較。

比較 -a 與 &&, -o 與 ||, ! test 與 test !

范例:要求某文件可執(zhí)行且有內(nèi)容,用 -a 和 && 分別實現(xiàn)

$ cat > test.sh
#!/bin/bash
echo "test"
[CTRL+D]  # 按下組合鍵CTRL與D結(jié)束cat輸入,后同,不再注明
$ chmod +x test.sh
$ if test -s test.sh -a -x test.sh; then echo "YES"; else echo "NO"; fi
YES
$ if test -s test.sh && test -x test.sh; then echo "YES"; else echo "NO"; fi
YES

范例:要求某個字符串要么為空,要么和某個字符串相等

$ str1="test"
$ str2="test"
$ if test -z "$str2" -o "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi
YES
$ if test -z "$str2" || test "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi
YES

范例:測試某個數(shù)字不滿足指定的所有條件

$ i=5
$ if test ! $i -lt 5 -a $i -ne 6; then echo "YES"; else echo "NO"; fi
YES
$ if ! test $i -lt 5 -a $i -eq 6; then echo "YES"; else echo "NO"; fi
YES

很容易找出它們的區(qū)別,-a-o 作為測試命令的參數(shù)用在測試命令的內(nèi)部,而 &&|| 則用來運算測試的返回值,! 為兩者通用。需要關(guān)注的是:

  • 有時可以不用 ! 運算符,比如 -eq-ne 剛好相反,可用于測試兩個數(shù)值是否相等; -z-n 也是對應(yīng)的,用來測試某個字符串是否為空
  • Bash 里,test 命令可以用[] 運算符取代,但是需要注意,[之后與] 之前需要加上額外的空格
  • 在測試字符串時,所有變量建議用雙引號包含起來,以防止變量內(nèi)容為空時出現(xiàn)僅有測試參數(shù),沒有測試內(nèi)容的情況

下面我們用實例來演示上面三個注意事項:

  • -ne-eq 對應(yīng)的,我們有時候可以免去 ! 運算

$ i=5
$ if test $i -eq 5; then echo "YES"; else echo "NO"; fi
YES
$ if test $i -ne 5; then echo "YES"; else echo "NO"; fi
NO
$ if test ! $i -eq 5; then echo "YES"; else echo "NO"; fi
NO
  • [ ] 可以取代 test,這樣看上去會“美觀”很多

$ if [ $i -eq 5 ]; then echo "YES"; else echo "NO"; fi
YES
$ if [ $i -gt 4 ] && [ $i -lt 6 ]; then echo "YES"; else echo "NO"; fi
YES
  • 記得給一些字符串變量加上 "",記得 [ 之后與 ] 之前多加一個空格

$ 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 expected
NO
$ if [ "$str" = "test" ]; then echo "YES"; else echo "NO"; fi
NO

到這里,條件測試就介紹完了,下面介紹命令列表,實際上在上面我們已經(jīng)使用過了,即多個test命令的組合,通過 &&||! 組合起來的命令序列。這種命令序列可以有效替換 if/then 的條件分支結(jié)構(gòu)。這不難想到我們在 C 語言程序設(shè)計中經(jīng)常做的如下的選擇題(很無聊的例子,但是有意義):下面是否會打印 j,如果打印,將打印什么?

#include <stdio.h>
int main()
{
    int i, j;

    i=5;j=1;
    if ((i==5) && (j=5))  printf("%d\n", j);

    return 0;
}

很容易知道將打印數(shù)字 5,因為 i==5 這個條件成立,而且隨后是 &&,要判斷整個條件是否成立,我們得進行后面的判斷,可是這個判斷并非常規(guī)的判斷,而是先把 j 修改為 5,再轉(zhuǎn)換為真值,所以條件為真,打印出 5 。因此,這句可以解釋為:如果 i 等于 5,那么把 j 賦值為 5,如果 j 大于 1 (因為之前已經(jīng)為真),那么打印出 j 的值。這樣用 && 連結(jié)起來的判斷語句替代了兩個 if 條件分支語句。

正是基于邏輯運算特有的性質(zhì),我們可以通過 &&|| 來取代 if/then 等條件分支結(jié)構(gòu),這樣就產(chǎn)生了命令列表。

命令列表

命令列表的執(zhí)行規(guī)律

命令列表的執(zhí)行規(guī)律符合邏輯運算的運算規(guī)律,用 && 連接起來的命令,如果前者成功返回,將執(zhí)行后面的命令,反之不然;用 || 連接起來的命令,如果前者成功返回,將不執(zhí)行后續(xù)命令,反之不然。

范例:如果 ping 通 www.lzu.edu.cn,那么打印連通信息

$ ping -c 1 www.lzu.edu.cn -W 1 && echo "=======connected======="

非常有趣的問題出來了,即我們上面已經(jīng)提到的:為什么要讓 C 程序在 main() 函數(shù)的最后返回 0 ?如果不這樣,把這種程序放入命令列表會有什么樣的結(jié)果?你自己寫個簡單的 C 程序,然后放入命令列表看看。

命令列表的作用

有時用命令列表取代 if/then 等條件分支結(jié)構(gòu)可以省掉一些代碼,而且使得程序比較美觀、易讀,例如:

范例:在腳本里判斷程序的參數(shù)個數(shù),和參數(shù)類型

#!/bin/bash

echo $#
echo $1
if [ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null);then
    echo "YES"
fi

說明:上例要求參數(shù)個數(shù)為 1 并且類型為數(shù)字。

再加上 exit 1,我們將省掉 if/then 結(jié)構(gòu)

#!/bin/bash

echo $#
echo $1
! ([ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null)) && exit 1

echo "YES"

這樣處理后,對程序參數(shù)的判斷僅僅需要簡單的一行代碼,而且變得更美觀。

小結(jié)

這一節(jié)介紹了 Shell 編程中的邏輯運算,條件測試和命令列表。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號