プロセス終了コードに基づく終了シェルスクリプト


378

いくつかのコマンドを実行するシェルスクリプトがあります。コマンドのいずれかがゼロ以外の終了コードで終了した場合、シェルスクリプトを終了させるにはどうすればよいですか?


3
私はあなたがbashを使用していると仮定して答えましたが、それが非常に異なるシェルである場合は、投稿で指定できますか?
マーティンW

難しい方法:$?コマンドごとにの値をテストします。簡単な方法:Bashスクリプトの上部set -eまたは#!/bin/bash -e上部に配置します。
mwfearnley

回答:


494

各コマンドの後に、終了コードが$?変数に含まれているため、次のようになります。

ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

はパイプ$?内の最後の要素の戻りコードを返すので、パイプ内のコマンドには注意が必要です。

ls -al file.ext | sed 's/^/xx: /"

ファイルが存在しない場合、エラーコードは返されません(sedパイプラインの一部が実際に機能し、0を返すため)。

bashシェルは、実際に、そのような場合に役立つことができる配列を提供されていますPIPESTATUS。この配列には、パイプラインコンポーネントごとに1つの要素があり、次のように個別にアクセスできます${PIPESTATUS[0]}

pax> false | true ; echo ${PIPESTATUS[0]}
1

これにより、falseパイプライン全体ではなく、コマンドの結果が得られることに注意してください。リスト全体を取得して、必要に応じて処理することもできます。

pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1

パイプラインから最大のエラーコードを取得する場合は、次のようなものを使用できます。

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

これは各PIPESTATUS要素を順番に通過しrc、前のrc値よりも大きかった場合はそれを格納します。


39
ポータブルコードの1行だけで同じ機能: ls -al file.ext || exit $?([[]]はポータブルではありません)
MarcH

19
MarcH、それ[[ ]]はでかなり移植可能であることがわかると思いますbash、これは質問がタグ付けされているものです:-)不思議なことに十分にls機能しないcommand.comため、移植性もありません。プレゼント。
paxdiablo

39
私はこれが古いことを知っていますが、配列を介してパイプ内のコマンドの終了コードを取得できることに注意してくださいPIPESTATUS(つまり、${PIPESTATUS[0]}最初のコマンド、${PIPESTATUS[1]}2番目のコマンド、または${PIPESTATUS[*]}すべての終了ステータスのリスト)
DevSolar

11
エレガントで慣用的なシェルスクリプトを$?直接調べる必要がほとんどないことを強調する必要があります。通常、if ls -al file.ext; then : nothing; else exit $?; fi@ MarcHが同等であると言うようなものを望みますがls -al file.ext || exit $?thenor else句が多少複雑な場合は、より保守しやすくなります。
tripleee

9
[[ $rc != 0 ]]あなたを与えるだろう0: not found1: not found、エラーを。これはに変更する必要があります[ $rc -ne 0 ]。またrc=$?、取り外して使用することもでき[ $? -ne 0 ]ます。
CurtisLeeBolin 2013年

223

$?を使用したい場合は、$?なので、各コマンドの後で確認する必要があります。各コマンドが終了するたびに更新されます。つまり、パイプラインを実行すると、パイプラインの最後のプロセスの終了コードのみが取得されます。

別のアプローチはこれを行うことです:

set -e
set -o pipefail

これをシェルスクリプトの先頭に置くと、bashがこれを処理するように見えます。以前の投稿者が指摘したように、「set -e」を指定すると、bashは単純なコマンドでエラーを出して終了します。「set -o pipefail」を指定すると、bashはパイプライン内のコマンドでもエラーで終了します。

この問題に関するもう少しの議論については、ここまたはここを参照してください。 これは、組み込みのセットに関するbashのマニュアルセクションです。


6
これが本当にトップの答えになるはずです。これを使用してPIPESTATUS、どこでも終了コードをチェックするよりもはるかに簡単です。
candu 2016

2
#!/bin/bash -eシェルスクリプトを開始する唯一の方法です。foo || handle_error $?実際に終了ステータスを調べる必要がある場合など、いつでも使用できます。
デイビスヘリング2017

53

set -e」はおそらくこれを行う最も簡単な方法です。それをプログラムのコマンドの前に置くだけです。


6
@SwaroopCH set -eスクリプト内のコマンドがエラーステータスで終了し、このエラーを処理しなかった場合、スクリプトは中止されます。
Andrew

2
set -e100%相当set -o errexitです。前者とは異なり、検索できます。公式ドキュメントについては、opengroup + errexitを検索してください。
MarcH 2018年

30

パラメータなしで単にbashでexitを呼び出すと、最後のコマンドの終了コードが返されます。ORと組み合わせると、前のコマンドが失敗した場合にのみ、bashはexitを呼び出す必要があります。しかし、私はこれをテストしていません。

コマンド1 || 出口;
コマンド2 || 出口;

Bashは、最後のコマンドの終了コードを変数$?にも格納します。


25
[ $? -eq 0 ] || exit $?; # exit for none-zero return code

3
これはexit $?(括弧なし)にすべきではありませんか?
13

21

http://cfaj.freeshell.org/shell/cus-faq-2.html#11

  1. どのように私はの終了コードを取得んcmd1でしcmd1|cmd2

    まず、cmd1終了コードがゼロ以外である可能性があり、それでもエラーを意味しないことに注意してください。これは例えば

    cmd | head -1

    141(またはksh93の場合は269)の終了ステータスが表示されるcmd1場合がありますが、これは、1行を読み取った後cmdhead -1終了すると、SIGPIPEシグナルによって中断された ためです。

    パイプラインの要素の終了ステータスを知るには cmd1 | cmd2 | cmd3

    a。zshの場合:

    終了コードは、pipestatus特殊配列で提供されます。 cmd1終了コードは$pipestatus[1]cmd3終了コードは $pipestatus[3]なので、$?常にと同じ $pipestatus[-1]です。

    b。bashで:

    終了コードはPIPESTATUS特別な配列で提供されます。 cmd1終了コードは${PIPESTATUS[0]}cmd3終了コードは ${PIPESTATUS[2]}なので、$?常にと同じ ${PIPESTATUS: -1}です。

    ...

    詳細については、次のリンクを参照してください


19

bashの場合:

# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;

#
# ... the rest of the script goes here
#  

function catch_errors() {
   # do whatever on errors
   # 
   #
   echo "script aborted, because of errors";
   exit 0;
}

19
おそらく「0を出る」べきではありません。それが成功を示しているからです。
nobar

3
exit_code = $ ?; echo "スクリプトがエラーのため中止されました"; exit $ exit_code
RaSergiy

1
@ HAL9001これの証拠はありますか?IBMのドキュメントには別の記載があります
Patrick James McDougle 2014

11

bashではこれは簡単です、それらを&&で結びます:

command1 && command2 && command3

ネストされたif構文を使用することもできます。

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi

+1これは私が探していた最も簡単な解決策でした。さらに、if (! command)コマンドからゼロ以外のエラーコードが予想される場合は、書き込むこともできます。
Berci

これはシーケンシャルコマンド用です。これらの3つを同時に起動し、いずれか1つが失敗した場合に全員を殺したい場合はどうしますか?
Vasile Surdu

4
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
    cmd="$@" ;

    doLog "DEBUG running cmd or exit: \"$cmd\""
    msg=$($cmd 2>&1)
    export exit_code=$?

    # if occured during the execution exit with error
    error_msg="Failed to run the command:
        \"$cmd\" with the output:
        \"$msg\" !!!"

    if [ $exit_code -ne 0 ] ; then
        doLog "ERROR $msg"
        doLog "FATAL $msg"
        doExit "$exit_code" "$error_msg"
    else
        #if no errors occured just log the message
        doLog "DEBUG : cmdoutput : \"$msg\""
        doLog "INFO  $msg"
    fi

}
#eof func doRunCmdOrExit

2
使用する理由はめったにありません$*"$@"代わりに、スペースとワイルドカードを保持するために使用します。
デービスニシン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.