web-dev-qa-db-ja.com

エラー時にbashシェルスクリプトから自動終了

私はいくつかのシェルスクリプトを書いてきましたが、コマンドのどれかが失敗した場合にシェルスクリプトの実行を停止させることができればそれが役に立つと思います。例として以下を参照してください。

#!/bin/bash  

cd some_dir  

./configure --some-flags  

make  

make install

そのため、この場合、スクリプトが指定されたディレクトリに変更できない場合は、失敗した場合にその後に./configureを実行することは絶対に望ましくありません。

各コマンドに対してifチェックを行うことができることはわかっていますが(これは絶望的な解決策だと思います)、コマンドの1つが失敗した場合にスクリプトを終了させるグローバル設定はありますか?

520
radman

set -e ビルトインを使用してください。

#!/bin/bash
set -e
# Any subsequent(*) commands which fail will cause the Shell script to exit immediately

あるいは、コマンドラインで-eを渡すことができます。

bash -e my_script.sh

set +eを使ってこの動作を無効にすることもできます。

(*) 注意:

失敗したコマンドがwhileまたはuntilキーワード、ifまたはその後のテストの一部)に続くコマンドリストの一部である場合、シェルはnotを終了します。 Elif予約語、&&または||リストの最後の&&または||に続くコマンドを除く)で実行されるコマンドの一部最後のパイプライン、またはコマンドの戻り値が)で反転されている場合

man bashから)

813
Adam Rosenfield

コマンドの1つが失敗したらすぐにスクリプトを終了するには、最初にこれを追加します。

set -e

これにより、テストの一部ではないコマンド(if [ ... ]条件や&&構文など)がゼロ以外の終了コードで終了すると、スクリプトは直ちに終了します。

62
sth

これを行う方法は次のとおりです。

#!/bin/sh

abort()
{
    echo >&2 '
***************
*** ABORTED ***
***************
'
    echo "An error occurred. Exiting..." >&2
    exit 1
}

trap 'abort' 0

set -e

# Add your script below....
# If an error occurs, the abort() function will be called.
#----------------------------------------------------------
# ===> Your script goes here
# Done!
trap : 0

echo >&2 '
************
*** DONE *** 
************
'
47
supercobra

pipefailと組み合わせて使用​​してください。

set -e
set -o pipefail

-e(errexit):コマンドがゼロ以外のステータスで終了すると、最初のエラーでスクリプトを中止します(untilループまたはwhileループ、if-tests、list構成要素を除く)。

-o pipefail:パイプラインで、ゼロ以外の戻り値を返したパイプ内の最後のコマンドの終了ステータスを返します。

第33章オプション

31

最初の行に収まる、受け入れられた答えに対する代替案:

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install
20
Petr Peller

1つの慣用句は次のとおりです。

cd some_dir && ./configure --some-flags && make && make install

これには時間がかかることがありますが、より大きなスクリプトでは論理関数に分割することができます。

18

私はあなたが探しているのはtrapコマンドだと思います。

trap command signal [signal ...]

詳細については、 このページ を参照してください。

もう1つの選択肢は、スクリプトの先頭にset -eコマンドを使用することです。プログラムやコマンドがtrue以外の値を返した場合は、スクリプトが終了します。

17
a_m0d

既存の回答で見逃していた1つの点は、エラートラップを継承する方法を示すことです。 bashシェルは、setを使用するためのそのようなオプションを1つ提供します。

-E

設定すると、ERRのトラップは、シェル関数、コマンド置換、およびサブシェル環境で実行されたコマンドに継承されます。 ERRトラップは通常 not から継承されています。


Adam Rosenfieldの答えset -eを使用することの推奨は場合によっては正しいですが、それ自体に潜在的な落とし穴があります。参照 GreyCatのBashFAQ - 105 - set -e(またはset -o errexit、またはtrapのERR)が期待どおりに動作しないのはなぜですか?

マニュアルによると、set -eは終了します

単純なコマンドが0以外のステータスである場合失敗したコマンドが while または until キーワード、 test in a if statement の一部、 && の一部の直後のコマンドリストの一部である場合、シェルは終了しません。 ||final && or ||any command in a pipeline but the last に続くコマンドを除いたリスト、または ! を介してコマンドの戻り値が反転されている場合。

つまり、set -eは次のような単純な場合には機能しません(詳細な説明はWikiにあります)。

  1. 算術演算子letまたは$((..))bash 4.1以降)を使用して、次のように変数値を増分します。

    #!/usr/bin/env bash
    set -e
    i=0
    let i++                   # or ((i++)) on bash 4.1 or later
    echo "i is $i" 
    
  2. 問題のコマンドが、最後のコマンドのnot部分である場合は、&&または||を介して実行されます。例えば下の罠は予想通りには発射しないでしょう

    #!/usr/bin/env bash
    set -e
    test -d nosuchdir && echo no dir
    echo survived
    
  3. ifステートメントで誤って使用された場合、ifステートメントの終了コードは最後に実行されたコマンドの終了コードです。以下の例では、最後に実行されたコマンドはechoで、これはtest -dが失敗した場合でもトラップを起動しません。

    #!/usr/bin/env bash
    set -e
    f() { if test -d nosuchdir; then echo no dir; fi; }
    f 
    echo survived
    
  4. inherit_errexitbashで設定されていない限り、コマンド置換で使用された場合、それらは無視されます。

    #!/usr/bin/env bash
    set -e
    foo=$(expr 1-1; true)
    echo survived
    
  5. exportdeclaretypesetlocalなど、代入には似ているがそうではないコマンドを使用する場合。ここでfへの関数呼び出しは、localが以前に設定されたエラーコードを一掃したため、not exitします。

    set -e
    f() { local var=$(somecommand that fails); }        
    g() { local var; var=$(somecommand that fails); }
    
  6. パイプラインで使用され、問題のあるコマンドが最後のコマンドのnotの部分である場合。例えば下記のコマンドはまだ通過するでしょう。 1つの選択肢は、first failedプロセスの終了コードを返すことによってpipefailを有効にすることです。

    set -e
    somecommand that fails | cat -
    echo survived
    

理想的な推奨はnotを使いset -eを使い、代わりに独自のバージョンのエラーチェックを実装することです。 Bashスクリプトでエラーを発生させる - の私の答えの1つにカスタムエラー処理を実装することに関するさらなる情報

0
Inian