コマンドエラーでbashスクリプトの関数を自動実行する方法はありますか?


12

私は一連のコマンドを実行する必要があるシェルスクリプトを書いており、すべてのコマンドは以前のすべてのコマンドに依存しています。コマンドが失敗した場合、スクリプト全体が失敗し、exit関数を呼び出します。各コマンドの終了コードを確認することはできましたが、有効にできるモードがあるかどうか、またはbashに自動的にそれを実行させる方法があるかどうか疑問に思っています。

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

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


これらのコマンドのいずれかが失敗した場合にmyfuncを呼び出す必要があることを、これらのコマンドを実行する前に何らかの方法でシェルに通知できる方法はありますか?

cd foo
rm a
cd bar
rm b

回答:


13

bash トラップERRを使用して、コマンドがゼロより大きいステータスを返した場合にスクリプトを終了し、終了時に関数を実行できます。

何かのようなもの:

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

trap 'myfunc' ERR

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

bashトラップ ERRは暗黙的であるset -o errexitset -e、POSIXではないことに注意してください。

そして、ERR失敗したコマンドがuntilwhileキーワードの直後のコマンドリストの一部、ifまたはのelif予約語に続くテストの一部、&&または||リストで実行されたコマンドの一部である場合、またはコマンドの戻りステータスが!


1

受け入れられた回答の(おそらく)より単純なバリエーション:

  1. set -e 1つのコマンドを失敗させてリストの実行を中止するために使用します。
  2. コマンドをリストするだけです。
  3. 使用if- then- elseあなたのエラー処理コマンド(複数可)を実行するためのステートメントを。この最後の部分は少しトリッキーです。見る:
セット-e
もし
    cmd 1                         #例、cd foo
     cmd 2                         #例、rm a
     cmd 3                         #例、cd bar
     cmd 4                         #例、rm b
その後
    + eを設定
    成功時に実行するコマンド(存在する場合)
そうしないと
    + eを設定
    myfunc
    失敗した場合に実行するその他のコマンド(存在する場合) 
fi

トリッキーな部分は、あなたがあなたのコマンドを入れていることである 一部ifif- - thenelseないthen一部またはelse一部。ことを思い出してほしいの構文if文があります

 リストの場合 ; 次に リストします。[elif  リスト ; 次に リストします。] ... [else  list ; ] fi 
   ↑↑↑↑
set -e場合は、そのシェルに指示()が失敗し、それが上に行くと実行してはならない (と、そのラインの下に)。これがシェルスクリプトの最も外側のレベルのコマンドで発生した場合、シェルは終了します。しかしながら、・・・ 下記(化合物)のリストであり、これらの4つのコマンドのいずれかの故障は、単に失敗するリスト全体せる-せる節が実行されるが。4つのコマンドすべてが成功すると、句が実行されます。cmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

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

set -e、POSIX仕様で指定および説明されています。


0

すべてのコマンドは前のすべてのコマンドに依存します。コマンドが失敗した場合、スクリプト全体が失敗するはずです」という言葉を考えれば、エラーを処理するための特別な機能は必要ないと思います。

必要なのは、&&オペレーターと||オペレーターでコマンドをチェーンすることだけです。

たとえば、前のコマンドのいずれかが壊れた場合(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で終了した場合に次のコマンドが実行されるということです。

チェーン内のいずれかのコマンドで問題が発生した場合は、コマンド/スクリプト/以下のすべてが|| 実行されます。

記録のためだけに、壊れたコマンドに応じて異なるアクションを実行する必要がある場合は、クラシックスクリプト$?を使用して、直前のコマンドの終了コードを報告する値を監視することで実行することもできます(コマンドが正常に実行された場合はゼロを返します)または、コマンドが失敗した場合は他の正の数)

例:

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

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