「トラップ…INT TERM EXIT」は本当に必要ですか?


63

クリーンアップタスクにtrap使用する多くの例trap ... INT TERM EXIT。しかし、3つのsigspecをすべてリストすることは本当に必要ですか?

マニュアルによると:

SIGNAL_SPECがEXIT(0)の場合、シェルの終了時にARGが実行されます。

スクリプトが正常に終了したか、または受信したSIGINTか終了したかに関係なく適用されると信じていますSIGTERM。また、実験は私の信念を確認します:

$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+  Interrupt               ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+  Terminated              ./trap-exit

では、なぜそんなに多くの例がすべてをリストしているのINT TERM EXITでしょうか?または、私は何かをEXIT見逃しましたか?足裏が見逃す場合がありますか?


3
またINT TERM EXIT、クリーンアップコードのような仕様では、SIGTERMまたはSIGINTを受け取ったときに2回実行されることに注意してください。
maxschlepzig

回答:


19

POSIX仕様は、それが実行されたときに、その環境がどのように見えるしなければならないかについて、EXITのトラップを実行するに生じる状態についてはあまり言いません。

Busyboxのアッシュシェルでは、SIGINTまたはSIGTERMのいずれかが原因で、終了前にトラップ終了テストが「TRAP」をエコーし​​ません。他のシェルも存在しているのではないかと思うでしょう。

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 

3
dashまたEXIT、受信したときにトラップしませんSIGINT/SIGTERM
maxschlepzig

4
zsh同様に-したがって、おそらくシグナルも一致するbash唯一のシェルEXITです。
maxschlepzig

@maxschlepzig zshEXIT受信時にトラップしませんINTが、受信時にトラップしますTERM。編集:私はちょうどこれが何歳であったことに気づいた
...-JoL

27

はい、違いがあります。

このスクリプトは、を押すEnterか送信するSIGINTか、または終了すると終了しますSIGTERM

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

を押すと、このスクリプトは終了しますEnter

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

* shBash、およびZshでテスト済み。(実行するトラップのコマンドを追加すると、shで機能しなくなります)


@Shawnが言ったこともあります:AshDashはでシグナルをトラップしませんEXIT

したがって、信号を堅牢に処理するには、トラップをEXIT完全に回避し、次のようなものを使用するのが最善です。

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup

1
クリーンアップによるソリューションは正しいことを行います-非常にエレガントです!mktemp呼び出しを伴う私のbashスクリプトのイディオムになりました。
ビョルンダールグレン

2
それはexit必要cleanupですか?
jarno 16

3
コード内にシェルスクリプトエラーがあり、それが途中で終了する場合、これは機能しません。
ijw

2
@ijw:BashとKshでは、それERRを処理するためにトラップできますが、移植性はありません
ザズ

5
このソリューションは、別のシェルがそれを呼び出すと堅牢ではありません。協調出口での待機は処理しません。trap - INT TERM; kill -2 $$クリーンアップの最後の行として、早めに終了したことを親シェルに伝えたいでしょう。親シェルfoobar.shがスクリプト(foo.sh)を呼び出し、次にbar.shを呼び出す場合、INT.TERMがfoo.shに送信された場合、bar.shを実行したくありません。 trap cleanup EXITこの伝播は自動的に処理されるため、IMOが最も堅牢です。またcleanup、スクリプトの最後に呼び出す必要がないことも意味します。
ニコラスピピトーネ

12

問題があるため、最後の答えを改良します。

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

上記のポイント:

INTハンドラーとTERMハンドラーは、テスト時に終了しません。エラーを処理した後、シェルが終了に戻ります(これは驚くべきことではありません)。したがって、クリーンアップは後で終了することを確認し、信号の場合は常にエラーコードを使用します(通常の終了の場合は、エラーコードを保存します)。

bashでは、INTハンドラーで終了するとEXITハンドラーも呼び出されるため、終了ハンドラーをアントラップして自分で呼び出します(動作に関係なくシェルで動作します)。

シェルスクリプトは最後に到達する前に終了することがあるため、exitをトラップします-構文エラー、set -eおよびゼロ以外の戻り値、単にexitの呼び出し。一番下にあるシェルスクリプトに頼ることはできません。

SIGQUITは、試したことがない場合はCtrl- \です。ボーナスコアダンプを取得します。だから、たとえそれが少しあいまいなものであっても、トラップする価値があると思います。

過去の経験では、(私のように)常にCtrl-Cを数回押すと、シェルスクリプトのクリーンアップ部分の途中でキャッチされることがあるので、これは機能しますが、必ずしも完全に望んでいるとは限りません。


2
呼び出し元は、終了の原因となった信号に関係なく、終了コードとして1を取得しますがtrap、呼び出し元はSIGINTで130、SIGTERMで143などを取得します。したがって、正しい終了コードをキャプチャして渡しますsig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }
-musiphil

2
trap '' EXIT INT TERMクリーンアップ機能の目的を明確にできますか?これは、前の段落で言及したユーザーのクリーンアップの偶発的な中断を防ぐためですか?EXIT冗長ではありませんか?
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.