コマンドエラーが発生した場合、bashスクリプトの関数を自動的に実行する方法はありますか?

コマンドエラーが発生した場合、bashスクリプトの関数を自動的に実行する方法はありますか?

私は複数のコマンドを実行する必要があるシェルスクリプトを作成していますが、各コマンドは以前のすべてのコマンドによって異なります。コマンドが失敗すると、スクリプト全体が失敗し、終了関数を呼び出します。各コマンドの終了コードを確認できますが、有効にするモードがあるかどうか、またはbashにこの操作を自動的に実行させる方法があるかどうかを知りたいです。

たとえば、次のコマンドを使用します。

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


このコマンドを実行する前にコマンドのいずれかが失敗した場合は、myfuncを呼び出して次のようにきちんと作成できるようにシェルにシグナルを送信する方法はありますか?

cd foo
rm a
cd bar
rm b

答え1

バッシュが使えますトラップエラーコマンドがゼロより大きい状態を返す場合は、スクリプトを終了し、終了時に関数を実行します。

それは次のとおりです。

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

気づくスラムトラップ ERR暗黙的なset -o errexitorset -eと は POSIX ではありません。

失敗したコマンドが、またはキーワードの直後のコマンドリストの一部であるか、予約語の後のテストの一部であるか、リストで実行されたコマンドの一部である、またはコマンドの戻り状態が反転を使用している場合、ERRトラップは実行されません。untilwhileifelif&&||!

答え2

許容される答えの(おそらく)より単純なバリエーションです。

  1. set -e コマンドが失敗し、リストの実行が中断されるようにするために使用されます。
  2. コマンドをリストするだけです。
  3. if- then- 文を使用してelseエラー処理コマンドを実行します。最後の部分が少しトリッキーですね。より:
セット
もし
    コマンド1                        #例えば、cd foo
    コマンド2                        # たとえば、rm a
    コマンド3                        #例えばCDバー
    コマンド4                        # たとえば、rm b
それから
    セット+e
    成功時に実行するコマンド(存在する場合)
その他
    セット+e
    私の機能
    失敗した場合に実行する追加コマンド(存在する場合)
フィリピン諸島

難しい部分は、コマンドを入力することです。 入力するそのif部分部分または部分の代わりに- if-thenを使用してください。それを覚えてelsethenelseifお問い合わせ構文はい

もし リスト;それから リスト;[イルライフ リスト;それから リスト;] ... [その他 リスト;]ペイ
   ↑↑↑↑
set -e()が失敗した場合は、()実行を続行しないことをシェルに伝えます。シェルスクリプトの最も外側のコマンドでこれが発生すると、シェルは終了します。しかし...以降は(複合)リストで、その後にはcmd1cd foocmd2rm acmd1cmd2cmd3cmd4if、これら4つのコマンドのいずれかが失敗すると、リスト全体が失敗し、対応する句がelse実行されます。 4つのコマンドがすべて成功すると、thenこの句が実行されます。

eどちらの場合も、最初にすべきことは、次の手順を実行してオプションを無効にする(オフにする)ことですset +e。そうしないと、コマンドが失敗するとmyfuncスクリプトが水から飛び出す可能性があります。

set -eはいPOSIX仕様で指定および説明

答え3

約束を守ってください」各コマンドは、前のすべてのコマンドによって異なります。コマンドが失敗すると、スクリプト全体が失敗します。「文字通りエラーを処理するために特別な機能が必要だとは思わない。

あなたがしなければならないのは、&&演算子と||演算子を使用してコマンドを連結することだけで、それがまさにあなたが書くことです。

たとえば、次のような場合、チェーンが壊れて「何かが間違っています」と印刷されます。どの前のコマンドが破損しています(Bashは左から右に読みます)。

cd foo && rm a && cd bar && rm b || echo "something went wrong"

実際の例(実際のデモのためにdir foo、file a、dir bar、およびfile bを作成しました):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

最後に、すべてのコマンドが正常に実行されると(終了コード0)、スクリプトは引き続き実行されます。

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

&& を使用すると、前のコマンドがコード 0 で終了すると、次のコマンドが実行されます。これは bash の成功を意味することを覚えておくことが重要です。

チェーンのコマンドの1つが間違っている場合は、command/script/||次のすべてのコマンドが実行されます。

記録のために中断されたコマンドに従って他の操作を実行する必要がある場合は、以前のコマンドの終了コードを報告する$?値を監視して、既存のスクリプトを使用してこれを実行することもできます(コマンドが正常に実行されたか0を返す場合)。コマンドがそうでなければ正数です)

例:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

出力:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

ヒント:アプリでは、「bash:cd:bbar:No such file ...」というメッセージが表示されない場合があります。eval $comm 2>/dev/null

関連情報