迴圈

迴圈 在 Bash

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

關於 迴圈

Bash 有兩種迴圈形式:

  • _針對一串項目中的每個項目_重複執行某些指令,
  • _在某個條件為 true(或 false)時_重複執行某些指令。

for迴圈

若要疊代一串項目,我們會使用for迴圈。

for varname in words; do COMMANDS; done

words會透過單字分割與檔名展開來展開(我們在引號概念中學過)。 每個單字會依序被指定給varname,然後執行 COMMANDS。

以下是一些範例,說明for迴圈在什麼時候很有用:

  • 對一群檔案執行某些指令。

    # source all the bash library files in the current directory
    for file in ./*.bash; do
        source "$file"
    done
    
  • 對指令碼或函式的每個參數執行某些指令。

    for arg in "$@"; do
        echo "argument: ${arg}"
    done
    
  • 對某個指令輸出中每個以空白分隔的單字執行某些指令。

    for i in $(seq 10); do
        echo "${i} squared is $((i * i))"
    done
    

    我們之後會看到一些關於這種寫法的警告。

算術for迴圈

Bash 確實有算術迴圈。 這對學過其他程式語言的人來說會有點熟悉。

for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done

bash 中的雙重小括號代表算術情境(我們會在之後的概念中看到更多關於 bash 算術的內容)。 上面使用seq的範例可以寫成這樣

for ((i = 1; i <= 10; i++)); do
    echo "${i} squared is $((i * i))"
done

什麼時候不該使用for

別用for讀取行。

這是你經常會看到的反面模式:對cat的輸出進行疊代。

for line in $(cat some.file); do 
    do_something_with "$line"
done

這麼做有兩個錯誤。

  1. 指令替換沒有加上引號,所以會受到單字分割影響。 單字分割預設會依_空白_分割,而不只是換行。 這個for迴圈疊代的是檔案中的單字,而不是行。
  2. 指令替換沒有加上引號,所以會受到檔名展開影響。 檔案中的每個單字都會被當成 glob 模式來比對。

如同我們在引號概念中學到的,單字分割可以用IFS變數控制,檔名展開也可以關閉。 但這往往是脆弱的做法。 for不是疊代檔案各行的正確方法。 讀取檔案的慣用做法是使用while迴圈。

while迴圈

使用while在_某個條件為 true 時_重複執行一連串指令。

while CONDITION_COMMANDS; do COMMANDS; done

就像if一樣,CONDITION_COMMANDS 並沒有特殊的語法(我們在條件判斷概念中學過)。 指令清單的結束狀態會決定是 true 還是 false。

控制迴圈

break指令會跳出迴圈。 控制權會從迴圈done結束符號之後的指令繼續。

continue指令會跳到迴圈的下一次疊代。

讀取檔案的行

如前所述,while迴圈是讀取檔案的慣用做法。

while IFS= read -r line; do
    do_something_with "$line"
done < some.file
  • 檔案的內容透過<重導向作為迴圈的輸入。

  • read指令若能從輸入讀取完整的一行,就會回傳 true。 當輸入被讀完,或最後一行結尾沒有換行符號時,read 會回傳 false。

    • 讀取檔案各行_真正_慣用的做法,即使最後一行結尾沒有換行符號也可以,是

      while IFS= read -r line || [[ -n "$line" ]]; do ...
      
  • -r選項會告訴read不要替換反斜線序列:反斜線就只是一般的字元。

  • IFS=只在read指令執行期間把空字串指定給 IFS。 這會暫時關閉單字分割,讓傳入的那一行文字中開頭或結尾的空白都能保留下來。

until

until結構會在某個條件為false時重複執行一連串指令。 迴圈會重複,_直到_條件變成 true 為止。

until CONDITION_COMMANDS; do COMMANDS; done

很少會用到until。 更常見的是使用「while not」迴圈。

while ! CONDITION_COMANDS; do ...

無限迴圈

有時你會想讓迴圈永遠執行下去。 這裡有兩種做法喔。

  1. 條件永遠是成功結束狀態的while迴圈

    while true; do ...
    
  2. 運算式留空的算術for迴圈

    for ((;;)); do ...
    

Do-While

沒有明確的 do-while 結構,但你可以達到相同的效果:

while true; do
    COMMANDS

    if END_CONDITION; then
        break
    fi
done
透過 GitHub 編輯 連結會在新視窗或分頁中開啟