條件

條件式 在 Bash

{other: "%{count} 個練習"}

關於 條件式

Bash 中的真假判斷

Bash 沒有布林值的概念。 它只有字串和數字,以及由字串或數字組成的陣列。 那麼,條件指令又是如何看待 true 和 false 的呢?

退出狀態

指令在結束時會產生一個_退出狀態_。 退出狀態是一個 0 到 255(含)之間的整數。 Bash 認為退出狀態為零代表_成功_。 任何其他的退出狀態則代表_失敗_。

這適用於所有指令,包括 bash 內建指令、關鍵字和函式。

「if」指令

if 指令的基本語法如下

if CONDITIONAL_COMMANDS; then TRUE_COMMANDS; else FALSE_COMMANDS; fi

它從 if 開始,條件指令與「成功」指令之間以 then 分隔,並以 fi 結束。 else 子句是選用的。

CONDITIONAL_COMMANDS 可以是單一指令,也可以是一份指令清單。

CONDITIONAL_COMMANDS 會被執行,然後:

  • 如果退出狀態為零(成功),就會執行 TRUE_COMMANDS。
  • 如果退出狀態非零(失敗),就會執行 FALSE_COMMANDS(如果有的話)。

可以用 elif 串接多層分支

if CONDITIONAL_COMMANDS
then TRUE_COMMANDS
elif CONDITIONAL_COMMANDS_2
then TRUE_COMMANDS_2
# more elif branches ...
else FALSE_COMMANDS
fi

在 then、elif、else 和 fi 這些字之前,必須有一個分號或一個換行。

特別強調:控制流程走向的是條件指令的退出狀態。 舉例來說,grep 在找到符合的內容時回傳 0,找不到時回傳 1。 -q 選項會抑制輸出,只產生退出狀態。

if grep -q "my pattern" my_file; then echo "the pattern is found in the file"; fi

「[」與「test」指令

CONDITIONAL_COMMANDS 周圍並沒有什麼特殊語法。 你可能習慣看到像這樣的條件式:

if [ "$password" = "secure" ]; then
    echo "Welcome!"
fi

[ 並不是特殊語法。 它是一個_指令_,用來評估條件運算式並以成功或失敗的狀態結束。 就像所有指令一樣,它與引數之間必須有空白。

[ 其實是 test 指令的同義詞。 兩者完全相同,差別只在於 [ 的最後一個引數_必須_是 ]。

條件運算式

在 [ 和 ] 之間,你要寫入一個條件運算式。 一些常見的條件運算式包括:

[ -f "$filename" ]             # file operations
[ "$string1" = "$string2" ]    # string comparisons
[ "$num1" -eq "$num2" ]        # arithmetic comparisons

還有很多其他可用的運算;它們都列在手冊的 Bash 條件運算式 一節中。

Note

在上面的範例中,請注意所有變數都加了引號。 test 和 [ 是普通的指令,它們的引數就像其他任何指令一樣,會受到單字分割與檔名展開的影響。

這一點值得特別說明,因為條件運算式會根據你提供_多少個引數_而有不同的評估方式:

  • 0 個引數:退出狀態為非零(失敗)
  • 1 個引數:如果引數不是空的,退出狀態為零(成功);如果引數是空的,則為非零。
  • 2 個引數:第一個引數必須是一元運算子(例如 [ -z "$name" ]),或是 !(用來反轉單一引數測試的狀態)
  • 以及更多情形。

如果忘記加引號,可能會得到意想不到的結果:

str=""

# this prints "empty"
if [ -n "$str" ]; then echo "not empty"; else echo "empty"; fi

# leaving the variable unquoted results in incorrect "not empty" output
if [ -n $str ]; then echo "not empty"; else echo "empty"; fi

「[[」關鍵字

[[...]] 條件結構 不是_指令_,而是關鍵字。 這表示,雖然它的處理方式和其他指令一樣,但它有特殊的剖析規則。 [[ 特別的地方在於,在其中展開的變數不會受到單字分割或檔名展開的影響。 這表示,即使不加引號,這個指令的行為也會如你所預期。

if [[ -n $str ]]; then echo "not empty"; else echo "empty"; fi

[[ 支援 test 和 [ 能處理的所有條件運算式。 除此之外,[[ 還提供

  • =~ 正規表示式比對運算子,
  • == 和 != 可作為萬用字元模式的比對運算子,
  • && 和 || 可作為邏輯運算子(特殊剖析規則),
  • < 和 > 可作為「直接」字串比較運算子(特殊剖析規則:因為它們是重導向符號,在 [ 中必須加以跳脫)。

一般普遍認為,這些特殊功能帶來的好處太多,因此應該一律使用 [[。 (例如 Google Shell 風格指南。)

「case」指令

case 是另一種控制流程指令。 它就像其他語言中的「switch」敘述。

case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac

WORD 會逐一與每個 PATTERN 進行比對。 當其中一個符合時,就會執行對應的 COMMANDS。

read -p "Guess the secret word: " word
case "$word" in
    secret) echo "Yes, you guessed it!" ;;
    ??????) echo "That's the right number of letters." ;;
    s*)     echo "You guessed the first letter." ;;
    *)      echo "Not even close! Try again." ;;
esac

每個 COMMANDS 子句都必須以兩個分號 ;; 結尾。

Note
  1. 有些替代做法可以做到指令的貫穿執行,以及讓模式比對繼續進行。 詳情請參閱手冊。
  2. 回想一下,我們在引號概念的檔名展開一節中談過模式。
透過 GitHub 編輯 連結會在新視窗或分頁中開啟