dash と ksh および bash の終了トラップ

dash と ksh および bash の終了トラップ

これは、現在のディレクトリに一時ディレクトリを設定し、終了時にそれを削除するトラップを設定する簡単なスクリプトです。

#filename: script   
set -x  
trap 'rm -rf "$d"' exit
d=`TMPDIR=$PWD mktemp -d`
"$@"

ksh script sleep 100またはを実行しbash script sleep 100、 で中断するとC-C、トラップが実行され、ディレクトリが削除されます。 では機能しませんdash。なぜでしょうか? これはバグですか、それとも意図された動作ですか?

答え1

zshpdksh(ただし、 の最近のバージョンはmkshそこから派生したものではありません)yash、 Bourne シェルは のように動作しますdash

bash、、ksh88およびのみksh93mkshそれ以外の動作をします。

SIGINTPOSIX 仕様では正しい動作が何であるかは明確ではありませんが、シェルがシグナル(またはその他のシグナル)のデフォルト ハンドラーをオーバーライドできるという記述はありません。

EXITトラップ アクションは が呼び出されたときに評価される必要があると書かれていますが、私の知る限り、たとえば、または構文エラーや特殊な組み込みの失敗などのエラー条件exitの結果としてシェルが終了したときに評価される必要があるかどうかさえ書かれていません。set -eset -u

シグナルの受信時にトラップを実行できるようにするにはEXIT、シェルがそのシグナルにハンドラーをインストールする必要があります。

これがksh、、およびmkshの機能ですが、これらが処理するシグナルのリストは、bash3 つの実装間で異なります。3 つすべてに共通するシグナルはINT、、、、およびのみのようです。QUITTERMALRMHUP

トラップを何らかのシグナルで実行したい場合EXIT、移植性の高い方法はそれらのシグナルを自分で処理することです。

trap 'exit 1' INT HUP QUIT TERM ALRM USR1
trap 'cleanup' EXIT

ただし、このアプローチは では機能しません。 は、トラップ ハンドラーから が呼び出された場合、トラップzshを実行しません。EXITexit

また、信号による死亡を親に報告することもできません。

代わりに、次のようにすることもできます。

for sig in INT QUIT HUP TERM ALRM USR1; do
  trap "
    cleanup
    trap - $sig EXIT
    kill -s $sig "'"$$"' "$sig"
done
trap cleanup EXIT

cleanupただし、 の実行中にさらにシグナルが到着すると、が再度実行される可能性があることに注意してください。が複数回呼び出された場合に が正しく動作することを確認したり、実行中にシグナルを無視したりすることcleanupをお勧めします。cleanup

答え2

警告:exit動作は保証されません。代わりにEXIT

POSIX標準では、EXITシグナルの場合にもトラップを実行するかどうかは定義されておらず、Bourne ShellがEXITあなたが言及したケースではトラップを呼び出さないという事実を考えると、あなたが入力しているのは明らかです。未指定行動。

関連情報