有人可能會認為兩篇文章中的 &
意思差不多,但實際上並不是。雖然 第一篇文章討論瞭如何在命令末尾使用 & 來將命令轉到後臺執行[1],在之後剖析了流程管理,第二篇文章將 & 看作取用檔案描述符的方法[2],這些文章讓我們知道了,與 <
和 >
結合使用後,你可以將輸入或輸出引導到別的地方。
但我們還沒接觸過作為 AND 運運算元使用的 &
。所以,讓我們來看看。
& 是一個按位運運算元
如果你十分熟悉二進位制數操作,你肯定聽說過 AND 和 OR 。這些是按位操作,對二進位制數的各個位進行操作。在 Bash 中,使用 &
作為 AND 運運算元,使用 |
作為 OR 運運算元:
AND:
-
0 & 0 = 0
-
0 & 1 = 0
-
1 & 0 = 0
-
1 & 1 = 1
OR:
-
0 | 0 = 0
-
0 | 1 = 1
-
1 | 0 = 1
-
1 | 1 = 1
你可以透過對任何兩個數字進行 AND 運算並使用 echo 輸出結果:
-
$ echo $(( 2 & 3 )) # 00000010 AND 00000011 = 00000010
-
2
-
$ echo $(( 120 & 97 )) # 01111000 AND 01100001 = 01100000
-
96
OR(|
)也是如此:
-
$ echo $(( 2 | 3 )) # 00000010 OR 00000011 = 00000011
-
3
-
$ echo $(( 120 | 97 )) # 01111000 OR 01100001 = 01111001
-
121
說明:
(( ... ))
告訴 Bash 雙括號之間的內容是某種算術或邏輯運算。(( 2 + 2 ))
、 (( 5 % 2 ))
(%
是求模[3]運運算元)和 ((( 5 % 2 ) + 1))
(等於 3)都可以工作。$
提取值,以便你可以使用它。((2+3))
等價於 (( 2+3 ))
和 (( 2 + 3 ))
。(( 5 / 2 ))
,你會得到 2
;或者這樣 (( 2.5 & 7 ))
,但會得到一個錯誤。然後,在按位操作中使用除了整數之外的任何東西(這就是我們現在所討論的)通常是你不應該做的事情。提示: 如果你想看看十進位制數字在二進位制下會是什麼樣子,你可以使用 bc ,這是一個大多數 Linux 發行版都預裝了的命令列計算器。比如:
-
bc <<< "obase=2; 97"
這個操作將會把 97
轉換成十二進位制(obase
中的 o
代表 “output” ,也即,“輸出”)。
-
bc <<< "ibase=2; 11001011"
這個操作將會把 11001011
轉換成十進位制(ibase
中的 i
代表 “input”,也即,“輸入”)。
&& 是一個邏輯運運算元
雖然它使用與其按位表達相同的邏輯原理,但 Bash 的 &&
運運算元只能呈現兩個結果:1
(“真值”)和0
(“假值”)。對於 Bash 來說,任何不是 0
的數字都是 “真值”,任何等於 0
的數字都是 “假值”。什麼也是 “假值”同時也不是數字呢:
-
$ echo $(( 4 && 5 )) # 兩個非零數字,兩個為 true = true
-
1
-
$ echo $(( 0 && 5 )) # 有一個為零,一個為 false = false
-
0
-
$ echo $(( b && 5 )) # 其中一個不是數字,一個為 false = false
-
0
與 &&
類似, OR 對應著 ||
,用法正如你想的那樣。
以上這些都很簡單……直到它用在命令的退出狀態時。
&& 是命令退出狀態的邏輯運運算元
正如我們在之前的文章中看到的[2],當命令執行時,它會輸出錯誤訊息。更重要的是,對於今天的討論,它在結束時也會輸出一個數字。此數字稱為“傳回碼”,如果為 0,則表示該命令在執行期間未遇到任何問題。如果是任何其他數字,即使命令完成,也意味著某些地方出錯了。
所以 0 意味著是好的,任何其他數字都說明有問題發生,並且,在傳回碼的背景關係中,0 意味著“真”,其他任何數字都意味著“假”。對!這 與你所熟知的邏輯操作完全相反 ,但是你能用這個做什麼? 不同的背景,不同的規則。這種用處很快就會顯現出來。
讓我們繼續!
傳回碼 臨時 儲存在 特殊變數[5] ?
中 —— 是的,我知道:這又是一個令人迷惑的選擇。但不管怎樣,別忘了我們在討論變數的文章中說過[4],那時我們說你要用 $
符號來讀取變數中的值,在這裡也一樣。所以,如果你想知道一個命令是否順利執行,你需要在命令結束後,在執行別的命令之前馬上用 $?
來讀取 ?
變數的值。
試試下麵的命令:
-
$ find /etc -iname "*.service"
-
find: '/etc/audisp/plugins.d': Permission denied
-
/etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service
-
/etc/systemd/system/dbus-org.freedesktop.ModemManager1.service
-
[......]
正如你在上一篇文章中看到的一樣[2],普通使用者許可權在 /etc
下執行 find 通常將丟擲錯誤,因為它試圖讀取你沒有許可權訪問的子目錄。
所以,如果你在執行 find 後立馬執行……
-
echo $?
……,它將列印 1
,表明存在錯誤。
(註意:當你在一行中執行兩遍 echo $?
,你將得到一個 0
。這是因為 $?
將包含第一個 echo $?
的傳回碼,而這條命令按理說一定會執行成功。所以學習如何使用 $?
的第一課就是: 單獨執行 $?
或者將它儲存在別的安全的地方 —— 比如儲存在一個變數裡,不然你會很快丟失它。)
一個直接使用 ?
變數的用法是將它併入一串鏈式命令串列,這樣 Bash 執行這串命令時若有任何操作失敗,後面命令將終止。例如,你可能熟悉構建和編譯應用程式原始碼的過程。你可以像這樣手動一個接一個地執行它們:
-
$ configure
-
.
-
.
-
.
-
$ make
-
.
-
.
-
.
-
$ make install
-
.
-
.
-
.
你也可以把這三行合併成一行……
-
$ configure; make; make install
…… 但你要希望上天保佑。
為什麼這樣說呢?因為你這樣做是有缺點的,比方說 configure
執行失敗了, Bash 將仍會嘗試執行 make 和 sudo make install
——就算沒東西可 make ,實際上,是沒東西會安裝。
聰明一點的做法是:
-
$ configure && make && make install
這將從每個命令中獲取退出碼,並將其用作鏈式 &&
操作的運算元。
但是,沒什麼好抱怨的,Bash 知道如果 configure
傳回非零結果,整個過程都會失敗。如果發生這種情況,不必執行 make 來檢查它的退出程式碼,因為無論如何都會失敗的。因此,它放棄執行 make,只是將非零結果傳遞給下一步操作。並且,由於 configure && make
傳遞了錯誤,Bash 也不必執行make install
。這意味著,在一長串命令中,你可以使用 &&
連線它們,並且一旦失敗,你可以節省時間,因為其他命令會立即被取消執行。
你可以類似地使用 ||
,OR 邏輯運運算元,這樣就算只有一部分命令成功執行,Bash 也能執行接下來連結在一起的命令。
鑒於所有這些(以及我們之前介紹過的內容),你現在應該更清楚地瞭解我們在 這篇文章開頭[1]出現的命令列:
-
mkdir test_dir 2>/dev/null || touch backup/dir/images.txt && find . -iname "*jpg" > backup/dir/images.txt &
因此,假設你從具有讀寫許可權的目錄執行上述內容,它做了什麼以及如何做到這一點?它如何避免不合時宜且可能導致執行中斷的錯誤?下週,除了給你這些答案的結果,我們將討論圓括號,不要錯過了喲!
via: https://www.linux.com/blog/learn/2019/2/logical-ampersand-bash
作者:Paul Brown[7] 選題:lujun9972 譯者:zero-MK 校對:wxy