INT と ERR の両方がキャプチャされますが、コールバックは複数回実行されます。

INT と ERR の両方がキャプチャされますが、コールバックは複数回実行されます。

次のコードを使用してINTとERRをキャプチャします。

set -ex -o pipefail

dest=$(mktemp -d)
cd "$dest"

trap "echo; echo Clean up; rm -rf $dest" INT ERR
sleep 9999

クリーンコールバックを押すと^C複数回実行されます。

++ echo Clean up
Clean up
++ rm -rf /tmp/tmp.KYXL110516
++ echo

++ echo Clean up
Clean up
++ rm -rf /tmp/tmp.KYXL110516

これが予想される動作ですか?一度だけできますか?

答え1

INT信号とERR信号の両方を受信します。 SIGINT はsleepゼロ以外の戻りコードで終了します。ゼロ以外の戻りコードは、SIGERR トラップをトリガーします。

sigspecがERRの場合、パイプライン(単一の単純なコマンドで構成できます)、リスト、または複合コマンドがゼロ以外の終了状態を返すたびに、arg ...コマンドが実行されます。

個々のトラップを調べる例:

set -ex -o pipefail
trap "echo Clean up for INT" INT
trap "echo Clean up for ERR" ERR
sleep 9999

実行してCtrl-Cを押します。

+ trap 'echo Clean up for INT' INT
+ trap 'echo Clean up for ERR' ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ echo Clean up for ERR
Clean up for ERR

トラップを一度だけ呼び出す場合、1つのオプションはトラップERR内でトラップをリセットすることです。INT

...
trap "echo Clean up for INT; trap ERR" INT ERR
...

...結果は次のとおりです。

+ trap 'echo Clean up for INT; trap ERR' INT ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ trap ERR

答え2

ではbash簡単に置き換えることができますtrap "my-cleanup-command" EXIT。このトラップは、SIGINTの送信時など、スクリプトの終了時に実行されます。私の考えでは、これは通常よりエレガントなアプローチです...

bashに固有の事実を除いて。 EXIT トラップは POSIX で言及されていますが、その動作は指定されていません。他の多くのシェルでは、シグナルによってシェルがシャットダウンした場合、EXITトラップは実行されません。

https://unix.stackexchange.com/a/57960/29483

私は時々シェルスクリプトが本当に嫌いです:).


リンクされた回答を見ると、最初のトラップ後にコードが実行されないようにする低 bash 関連の方法は、すぐに終了することです。これには他の方法を使用することもできますが、これが私が試してみる最も簡単な方法です。

cleanup() {
    echo "Clean up"
    rm -rf "$dest"
}
trap cleanup EXIT
trap "exit 1" INT ERR

関連情報