コマンドがゼロ以外の値を返す場合、シェルスクリプトを中止しますか?


437

いくつかのコマンドを呼び出すBashシェルスクリプトがあります。コマンドのいずれかがゼロ以外の値を返す場合、シェルスクリプトが戻り値1で自動的に終了するようにしたいと思います。

各コマンドの結果を明示的にチェックせずにこれは可能ですか?

例えば

dosomething1
if [[ $? -ne 0 ]]; then
    exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
    exit 1
fi

8
に加えてset -eset -u(またはset -eu)も実行します。-u存在しない変数にアクセスして、診断なしで空白の値を生成できる、ばかげたバグを隠す動作に終止符を打つ。
Kaz

回答:


742

これをスクリプトの最初に追加します。

set -e

これにより、単純なコマンドがゼロ以外の終了値で終了した場合、シェルは即座に終了します。単純なコマンドとは、if、while、またはuntil testの一部ではない任意のコマンド、または&&または||の一部です。リスト。

詳細については、「set」内部コマンドのbash(1)のマニュアルページを参照してください。

私はほとんどすべてのシェルスクリプトを「set -e」で開始しています。途中で何かが失敗し、残りのスクリプトの前提が破られた場合、スクリプトを頑固に続行するのは本当に面倒です。


36
これでうまくいきますが、/ bin以外の場所からbashを頻繁に実行するため、 "#!/ usr / bin / env bash"を使用します。また、「#!/ usr / bin / env bash -e」は機能しません。さらに、デバッグのためにトレースをオンにしたいときに、「set -xe」を読み取るように変更できる場所があると便利です。
Ville Laurikari、2009年

48
また、スクリプトがとして実行された場合、シバン行のフラグは無視されbash script.shます。
トムアンダーソン

27
注:bashスクリプト内で関数を宣言する場合、この機能を拡張するには、関数本体で-eを再宣言する必要があります。
ジンキム・

8
また、スクリプトをソースとする場合、シバン行は無関係ではありません。

4
@JinKim bash 3.2.48ではそうではないようです。スクリプト内で次のことを試してくださいset -e; tf() { false; }; tf; echo 'still here'set -eの本体内になくてもtf()、実行は中止されます。おそらく、それはサブシェルset -e継承されていないと言うつもりでした。
mklement0 2013

202

受け入れられた回答に追加するには:

set -eパイプがある場合は特に、時には十分ではないことを覚えておいてください。

たとえば、次のスクリプトがあるとします。

#!/bin/bash
set -e 
./configure  > configure.log
make

...これは期待どおりに機能しますconfigure。エラーにより実行が中止されます。

明日は一見取るに足らない変更を加えます:

#!/bin/bash
set -e 
./configure  | tee configure.log
make

...そして今、それは機能しません。これはここで説明されており、回避策(Bashのみ)が提供されています。

#!/ bin / bash
セット-e 
set -o pipefail

./configure | tee configure.log
作る

1
pipefail一緒に行くことの重要性を説明していただきありがとうございますset -o
マルコム

83

例のifステートメントは不要です。次のようにしてください:

dosomething1 || exit 1

Ville Laurikariのアドバイスを取り入れて使用する場合set -eは、一部のコマンドでこれを使用する必要があります。

dosomething || true

|| trueコマンドパイプラインが持つようになりますtrueので、コマンドが失敗した場合でも、戻り値を-eオプションは、スクリプトを殺すことはありません。


1
私はこれが好き。特に、トップの回答はbash中心です(zshスクリプトに適用されるかどうか、またはどの程度適用されるかは、私にはまったくわかりません)。そして、私はそれを調べることもできますが、あなたがたはより明確です。なぜなら、論理だからです。
g33kz0r 2015年

set -eはbash中心ではありません-元のBourne Shellでもサポートされています。
Marcos Vives Del Sol

27

終了時に行う必要があるクリーンアップがある場合は、疑似信号ERRで「トラップ」を使用することもできます。これは、INTまたは他の信号をトラップするのと同じように機能します。コマンドがゼロ以外の値で終了すると、bashはERRをスローします。

# Create the trap with   
#    trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah

または、特に「set -e」を使用している場合は、EXITをトラップできます。トラップは、スクリプトが正常終了、割り込み、-eオプションによる終了など、何らかの理由で終了したときに実行されます。


12

$?変数はほとんど必要ありません。疑似イディオムcommand; if [ $? -eq 0 ]; then X; fiは、常にと書く必要がありますif command; then X; fi

$?必要なケースは、複数の値に対してチェックする必要がある場合です。

command
case $? in
  (0) X;;
  (1) Y;;
  (2) Z;;
esac

または$?再利用またはその他の操作が必要な場合:

if command; then
  echo "command successful" >&2
else
  ret=$?
  echo "command failed with exit code $ret" >&2
  exit $ret
fi

3
なぜ「常に次のように書くべき」なのでしょうか。つまり、なぜそうあるべきなのか?コマンドが長い場合(多数のオプションを指定してGCCを呼び出すことを考えてください)、戻りステータスを確認する前にコマンドを実行する方がはるかに読みやすくなります。
ysap 2012

コマンドが長すぎる場合は、名前を付けることで分割できます(シェル関数を定義します)。
マークエドガー

12

上部-eまたはset -e上部で実行します。

も見てくださいset -u


34
潜在的に他の人を救うために読む必要性help set-u設定されていない変数への参照をエラーとして扱います。
mklement0 2013

1
だから、それはどちらset -uset -e、または両方ですか?@lumpynose
ericn

1
@eric私は数年前に退職しました。私の仕事は好きでしたが、高齢の脳はすべてを忘れてしまいました。オフハンド両方を一緒に使用できると思います。私の言葉遣いが悪い。私は「および/または」を言っているべきだった。
lumpynose 2016年

3

のような表現

dosomething1 && dosomething2 && dosomething3

コマンドの1つがゼロ以外の値で戻ると、処理が停止します。たとえば、次のコマンドは「done」を出力しません。

cat nosuchfile && echo "done"
echo $?
1


-2

Mark Edgarsの入力に追加の質問があり、ここに追加の例があり、トピック全体に触れているので、参照用に別のものを投入するだけです。

[[ `cmd` ]] && echo success_else_silence

これはcmd || exit errcode誰かが示したのと同じです。

例えば。マウントされている場合、パーティションがマウント解除されていることを確認したい:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1 

5
いいえ、[[ cmd`]] `は同じものではありません。コマンドの終了ステータスに関係なく、コマンドの出力が空の場合はfalse、それ以外の場合はtrueです。
Gilles「SO-邪悪なことをやめ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.