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


13

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ステートメントが失敗した場合、スクリプト全体を終了するにはどうすればよいですか?

回答:


18

交換:

done

で:

done || exit 1

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

雑学のポイントとして、1in exit 1は必要ありません。プレーンなexitコマンドはfalse、ダウンロードが失敗した場合、最後に実行されたコマンドの終了ステータス(code = 1)で終了します。ダウンロードが成功した場合、ループの終了コードはechoコマンドの終了コードです。 echo通常はcode = 0で終了し、シグナル的に成功します。その場合、は||トリガーされず、exitコマンドは実行されません。

最後に、set -o errexit驚きに満ちていることに注意してください。長所と短所については、GregのFAQ#105をご覧ください。

ドキュメンテーション

からman bash

for((expr1; expr2; expr3)); リストを行う ; 行わ
まず、算術式expr1は、演算評価中の以下に記載の規則に従って評価されます。その後、算術式expr2は、ゼロと評価されるまで繰り返し評価されます。expr2がゼロ以外の値に評価されるたびに、listが実行され、算術式expr3が評価されます。式が省略された場合、1と評価されるかのように動作します 。戻り値は、リスト内の実行された最後のコマンドの終了ステータス、またはいずれかの式が無効な場合はfalseです。[エンファシスの追加]


truebreakステートメントの前に明示的に配置して、ループの終了値を確保するのは良い考えだと思いますか?
-RobertL

1
明示的は暗黙的よりも優れていると思います。だから、私が書いexit 1たのは、単純なものexitがうまくいったときだ。しかし、それはスタイルの問題であり、他の人は自分の意見を持つことができます。
ジョン1024

1
うまくいきます!おかげで:)個人的に私はexitプレーン出口として読むだろう-それはそれ自体でスクリプトを終了します。exit 1 は、他のプロセスへの「シグナル」(つまりerrexit)として読みます。つまり、の「結果」に基づいてスクリプトを終了しますexit 1。-だから私は行ったexitが、説明に感謝
-the_velour_fog

1
エラー条件のためにスクリプトが終了する場合は、を呼び出す必要がありますexit 1。それはまったく影響errexitしません。呼び出し元のプログラムに何か問題が発生したことを伝えるだけです。falseコマンドは、1つのステートメントが含まれていますexit(1)。Unixコマンドの99.9%は成功時に0を返し、エラー時に0以外を返します。あなたもそうすべきです。
-RobertL

2

errexit設定している場合、falseステートメントはスクリプトをすぐに終了させるはずです。curlコマンドが失敗した場合も同じです。

記述されているスクリプト例curlfalse、errexitが設定されている場合、最初の呼び出しで最初のコマンドが失敗した後に終了する必要があります。

それがどのように機能するかを見るために(私は短縮形を使用-eして設定しerrexitます:

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

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

したがって、curlコマンドが複数回実行される場合、このスクリプトはerrexit設定されていません。


1
set -eそれよりも微妙です。それはなりません、ループ内の最初の失敗したコマンドの後に終了します。実行(set -e; for (( i=1; i<5; i++ )); do echo $i; false; done || echo "FAIL"; )して、コードがfalse4回実行されることに注意することで、それを自分で証明できます。詳細についてはset -eGregのFAQ#105を参照してください。
John1024

@ John1024ありがとう。これは下に下がっていきます。
-RobertL

@ John1024しかし、証拠はまだerrexit設定されていなかったと思います。問題のスクリプトにロジックを適用してください。これを実行します:(set -e; for (( i=1; i<5; i++ )); do echo $i; false; done ; echo still here ) はいif while || &&などで戻り値をテストしても、errexitはトリガーされません 元のスクリプトは||forループではありませんでした。
-RobertL

set -o errexitサンプルコードにコマンドが表示されていないことに気付いたので、今すぐ追加しました。私にとっては、期待どおりに終了するエラーではありませんでした。falseforループの最後のコマンドとして保持し、ループを閉じる必要がありましたdone || exit [1]-それからうまくいきました!
-the_velour_fog

@RobertLあなたの主張がわかります。
ジョン1024

1

set -o errexit プロセスから抜け出す必要があるため、ループやサブシェルでは扱いにくい場合があります。

(通常の操作であっても)ループを壊すことは悪い習慣と見なされます。2つの条件について、for-loopよりもwhile-loopを好むように、昔ながらの学校と呼ぶことができますが、読む方が良いでしょう。

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

0

もしerrexitセットされ、curlコマンドが失敗した右curlコマンドの後にスクリプトが終了に失敗しました。bashマニュアルにはset -e、複合コマンド内の単一の失敗した戻りステータスを無視するヒントはありません。これは、複合コマンドset -eが無視されるコンテキストで実行される場合にのみ当てはまります。
https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin

RobertLによって投稿されたわずかに適合した例を試してください。これは、falseコマンドの直後の最初の反復で停止します。

( set -e; for (( i=1; i<5; i++ )); do echo $i; false; echo "${i}. iteration done"; done ; echo "loop done" )

0

curlコマンドに--failオプションを追加するだけで問題が解決します。jenkinsパイプラインでcurlを使用している場合、curlコマンドが失敗するとスクリプトは失敗し、エラーで終了します。

curl -LSso --fail ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.