如何根據 for 迴圈的結果使該腳本錯誤退出?

如何根據 for 迴圈的結果使該腳本錯誤退出?

我有一個 bash 腳本,它使用set -o errexit以便在出錯時整個腳本在失敗時退出。
該腳本運行的curl命令有時無法檢索預期的檔案 - 但是當發生這種情況時,腳本不會錯誤退出。

我已經添加了一個for循環

  1. 暫停幾秒鐘,然後重試該curl命令
  2. 在 for 迴圈的底部使用false來定義預設的非零退出狀態 - 如果curl 指令成功 - 迴圈將中斷且最後一個指令的退出狀態應為零。
#! /bin/bash

set -o errexit

# ...

for (( i=1; i<5; i++ ))
do
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    if [ -f ~/.vim/autoload/pathogen.vim ]
    then
        echo "file has been retrieved by curl, so breaking now..."
        break;
    fi

    echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
    sleep 5
    # exit with non-zero status so main script will errexit
    false

done

# rest of script .....

問題是當curl命令失敗時,循環會重試該命令五次 - 如果所有嘗試均不成功,則 for 循環結束並且主腳本恢復 - 而不是觸發errexit.
如果此語句失敗,如何讓整個腳本退出curl

答案1

代替:

done

和:

done || exit 1

for如果循環以非零退出程式碼退出,這將導致程式碼退出。

順便說一下,不需要1in 。如果下載失敗,exit 1普通exit命令將以最後執行的命令的退出狀態退出,該狀態將為(code=1)。false如果下載成功,循環的退出代碼就是echo指令的退出代碼。 echo通常以 code=0 退出,表示成功。在這種情況下,||不會觸發並且exit不會執行該命令。

最後,請注意,這set -o errexit可能充滿驚喜。有關其優缺點的討論,請參閱格雷格的常見問題 #105

文件

man bash

為了(( 表達式 1 ; 表達式 2 ; 表達式 3 )) ;列表 ; 完畢
首先,算術表達式 expr1 根據下方算術求值中所描述的規則求值。然後重複計算算術表達式 expr2,直到計算結果為零。每次 expr2 計算結果為非零值時,都會執行 list 並計算算術表達式 expr3。如果省略任何表達式,則其計算結果就像是 1。 傳回值是清單中執行的最後一個命令的退出狀態,如果任何表達式無效,則傳回 false。[強調已添加]

答案2

如果您希望在第一次失敗時停止循環,並避免set -e,您可以執行以下操作:

for i in `seq 1 10`; do
  run_command || exit 1;
done

答案3

如果您已errexit設置,則該false語句應導致腳本立即退出。如果curl命令失敗,同樣的情況。

您的範例腳本,如所寫,如果設定了 errexit,則應curl在第一次呼叫時在第一個命令失敗後退出。false

要查看它是如何工作的(我使用簡寫-e來設定errexit

$ ( set -e;  false; echo still here )
$

$ ( set +e;  false; echo still here )
still here
$

因此,如果該curl命令執行多次,則該腳本沒有errexit設定。

答案4

set -o errexit在循環和子 shell 中可能會很棘手,因為您必須從進程中返回。

打破循環(即使在正常操作中)被認為是不好的做法。你可以說我是老派,在兩個條件下更喜歡 while 循環而不是 for 循環,但我發現讀起來更好:

i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
    [ $i -eq 1 ] || sleep 5
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    RET=$?
    i=$((i+1))
done
exit $RET

相關內容