sigterm での bash の動作

sigterm での bash の動作

以下のようなスクリプトを用意します。

#!/bin/bash
#
# run this script.  don't run it if it's already running.
#

PIDFILE=/tmp/script.pid
LOGFILE=script.log


if [[ -f $PIDFILE ]]; then
    echo "$PIDFILE exists.  Not going to run..."
    exit 0
fi

sleep 10m >> $LOGFILE 2>&1 &
PID=$!
echo $PID > $PIDFILE

trap "echo Exiting...; rm $PIDFILE; exit $?" INT TERM EXIT KILL

wait $PID

このスクリプトを以下のように呼び出します:

timeout 2m ./test_script

タイムアウトまたは Ctrl + C の場合、スクリプトは「Exiting」を 2 回出力します。

# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove `/tmp/script.pid': No such file or directory

以下は strace の出力とその他のデータです。

# ps -ef | grep -v grep | egrep -i "sleep|time"
root      8571  4690  0 12:17 pts/0    00:00:00 timeout 2m ./test_script
root      8572  8571  0 12:17 pts/0    00:00:00 /bin/bash ./test_script
root      8573  8572  0 12:17 pts/0    00:00:00 sleep 10m
# cat /tmp/script.pid
8573
# strace -p 8571
Process 8571 attached - interrupt to quit
wait4(-1, 0x7fffc43fad4c, 0, NULL)      = ? ERESTARTSYS (To be restarted)
--- SIGINT (Interrupt) @ 0 (0) ---
kill(0, SIGINT)                         = 0
kill(0, SIGCONT)                        = 0
--- SIGCONT (Continued) @ 0 (0) ---
rt_sigreturn(0)                         = 61
--- SIGINT (Interrupt) @ 0 (0) ---
rt_sigreturn(0x2)                       = 61
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 8572
--- SIGCHLD (Child exited) @ 0 (0) ---
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
Process 8571 detached

スクリプトが信号を 2 回トラップして「Exiting...」を 2 回印刷する理由の内部を理解するのを手伝ってくれる人はいませんか?

答え1

trapステートメントを次の 3 行に置き換えた場合:

trap "echo Exiting... INT;  exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT

出力が得られます

Exiting... TERM
Exiting... EXIT

そこから推測できるのは

  • ステートメントtrap … TERMにより、シェルは SIGTERM シグナルをトラップします。timeoutコマンドは、タイムアウトが経過すると、プロセスに SIGTERM (デフォルト) を送信します。 そのため、シェルはシグナルをキャッチし、 、 (実際のスクリプト内)、 を含む指定されたコマンドを実行echormますexit
  • このtrap … EXITステートメントにより、シェルは「家に帰る前にこれを忘れずに実行してください」という付箋を残します。したがって、SIGTERM トラップがコマンドを実行するとexit、EXIT トラップが実行されます。
  • EXIT トラップがexitコマンドを実行すると、スクリプトは EXIT トラップを実行して再帰地獄に陥るのではなく、実際に終了します。

Ctrlスクリプトの実行中に+を入力するCと、INT トラップに続いて EXIT トラップが発生します。 を使用せずにスクリプトを実行するかtimeout、スリープ時間よりも長いタイムアウト期間でスクリプトを実行すると、EXIT トラップのみが発生します。

おそらくこう言うだけで十分でしょう

trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT

EXIT トラップは を実行する必要はないと私は考えています。なぜなら、コマンド (スクリプトの最後の暗黙のコマンドを含む)exitを実行することによって EXIT トラップに入るため、EXIT トラップ (および) の実行が完了すると、シェルは終了する以外に何もすることがなくなるからです。唯一の疑問は、スクリプトがどのような終了ステータスで終了するかということです。また、何らかの終了ステータス値を保存して を実行している場合は 、興味深い結果になるかもしれません。ただし、 について話している場合、スクリプトはおそらく の終了ステータスで終了するでしょう。そして、 が実行する最後のコマンドである場合は、デフォルトでそうなるでしょう。exitechormrm $PIDFILE; exit $saved_statusrm $PIDFILE; exit $?rmrm

私はいくつかの簡単なテストを行い、

trap "exit" INT TERM

コマンドですが、理解できません。YMMV。


PS トーマスが言ったように、trap … KILL効果がありません。

関連情報