web-dev-qa-db-ja.com

SIGINTなどをトラップするときに終了コードを保持しますか?

説明したようにtrapを使用すると、 on http://linuxcommand.org/wss0160.php#trap ctrl-c(または同様のもの)をキャッチし、終了する前にクリーンアップしてから、返される終了コードを変更します。

これはおそらく現実の世界では違いはありません(たとえば、終了コードは移植可能ではなく、その上に プロセスが終了したときのデフォルトの終了コード で説明されているように常に明確であるとは限らないため)それを防ぐ方法が本当になく、代わりに中断されたスクリプトのデフォルトのエラーコードを返す方法がないのだろうか?

例(bashですが、私の質問はbash固有のものとは見なされません):

#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _

出力:

$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT 
EXIT
$ echo $?
1

(それをよりPOSIX準拠にするために削除するように編集されました。)

(代わりにbashスクリプトにするために編集されましたが、私の質問はシェル固有ではありません。)

ポータブルではない「SIGINT」を支持して、トラップにはポータブルな「INT」を使用するように編集されました。

不要な中括弧を削除し、潜在的なソリューションを追加するために編集されました。

更新:

ハードコードされたいくつかのエラーコードで終了してEXITをトラップすることで解決しました。特定のシステムでは、エラーコードが異なるか、EXITトラップが不可能である可能性がありますが、私の場合は問題ありません。

trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
14
phk

EXITハンドラーを変更するだけですinsideクリーンアップハンドラー。次に例を示します。

#!/bin/bash
cleanup() {
    echo trapped exit
    trap 'exit 0' EXIT
}
trap cleanup EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. '
4
rocky

実際、bashの内部readを中断することは、bashが実行するコマンドを中断することとは少し異なるようです。通常、trapと入力すると、$?が設定され、それを保持して同じ値で終了できます。

trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT

sleepのようなコマンドまたはwaitのような組み込みコマンドを実行しているときにスクリプトが中断された場合、

130 SIGINT
130 EXIT

終了コードは130です。ただし、read -p、 そうみたいです $?は0です(とにかく私のバージョンのbash 4.3.42では)。


私のリリースの変更ファイルによると、read中のシグナルの処理は進行中の可能性があります...(/ usr/share/doc/bash/CHANGES)

このバージョンのbash-4.3-alphaと以前のバージョンのbash-4.2-releaseの間の変更点。

  1. Bashの新機能

    r。 Posixモードでは、トラップされた信号によって「読み取り」を中断できます。トラップハンドラーの実行後、readは128以上のシグナルを返し、部分的に読み取られた入力を破棄します。

9
meuh

通常のシグナル終了コードは$?トラップハンドラへのエントリ時:

sig_handler() {
    exit_status=$?  # Eg 130 for SIGINT, 128 + (2 == SIGINT)
    echo "Doing signal-specific up"
    exit "$exit_status"
}
trap sig_handler INT HUP TERM QUIT

別個のEXITトラップがある場合は、同じ方法を使用できます。シグナルハンドラー(存在する場合)から渡された終了ステータスを直ちにクリーンアップし、保存された終了ステータスを返します。

6
Tom Hale

エラーコードを返すだけでは、SIGINTによる終了をシミュレートするには不十分です。これまで誰もこれについて言及しなかったことに驚いています。さらに読む: https://www.cons.org/cracauer/sigint.html

適切な方法は次のとおりです。

for sig in EXIT ABRT HUP INT PIPE QUIT TERM; do
    trap "cleanup;
          [ $sig  = EXIT ] && normal_exit_only_cleanup;
          [ $sig != EXIT ] && trap - $sig EXIT && kill -s $sig $$
         " $sig
done

これはBash、Dash、zshで動作します。移植性をさらに高めるには、数値信号仕様を使用する必要があります(一方、zshはkillコマンドの文字列パラメーターを想定しています...)

EXIT信号の特別な扱いにも注意してください。これは、一部のシェル(つまりBash)が、任意のシグナルに対してEXITでトラップを実行するためです(そのシグナルでトラップが定義されている可能性があります)。 EXITトラップのリセットはそれを防ぎます。

「通常の」出口でのみコードを実行する

チェック[ $sig = EXIT ]は、通常の(シグナルなし)出口でのみコードの実行を許可します。ただし、all信号には、最後にEXITでトラップをリセットするトラップが必要です。 normal_exit_only_cleanupは、そうでないシグナルに対しても呼び出されます。また、set -eを介して出口によって実行されます。これは、ERR(Dashではサポートされていません)をトラップし、killの前にチェック[ $sig = ERR ]を追加することで修正できます。

簡略化されたBashのみのバージョン

一方、この動作は、Bashで簡単に実行できることを意味します

trap cleanup EXIT

クリーンアップコードを実行して終了ステータスを保持するだけです。

編集済み

  • 「EXITはすべてをトラップする」という精巧なBashの動作

  • トラップできないKILLシグナルを削除する

  • 信号名からSIGプレフィックスを削除

  • kill -s EXITを行わないでください

  • set -e/ERRを考慮に入れる

2
philipp2100