このスクリプトを for ループの結果に基づいてエラー終了させるにはどうすればよいですか?

このスクリプトを for ループの結果に基づいてエラー終了させるにはどうすればよいですか?

set -o errexitエラーが発生すると、スクリプト全体が障害発生時点で終了するようにする bash スクリプトがあります。
スクリプトはcurl、意図したファイルを取得できないことがあるコマンドを実行しますが、その場合、スクリプトはエラーで終了しません。

forループを追加しました

  1. 数秒間停止してからcurlコマンドを再試行してください
  2. falsefor ループの下部で使用して、デフォルトのゼロ以外の終了ステータスを定義します。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コマンドが失敗すると、ループがコマンドを 5 回再試行することです。すべての試行が失敗すると、 がトリガーされるのではなく、 for ループが終了し、メイン スクリプトが再開されますerrexit
このステートメントが失敗した場合にスクリプト全体を終了させるにはどうすればよいですかcurl?

答え1

交換する:

done

と:

done || exit 1

forこれにより、ループがゼロ以外の終了コードで終了した場合にコードが終了します。

トリビアとして、 は1必要exit 1ありません。プレーンexitコマンドは、最後に実行されたコマンドの終了ステータスで終了しますfalse。ダウンロードが失敗した場合は、(code=1) になります。ダウンロードが成功した場合、ループの終了コードはコマンドの終了コードになりますechoecho通常、 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

設定した場合errexitfalseステートメントによってスクリプトは直ちに終了します。コマンドが失敗した場合も同様です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ループやサブシェルでは、プロセスから抜け出す方法を渡さなければならないため、扱いにくくなる可能性があります。

ループを中断することは (通常の操作であっても) 悪い習慣とみなされます。2 つの条件に対して for ループではなく while ループを好むのは古い考え方だと言われるかもしれませんが、私は次のように読む方が良いと思います。

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

関連情報