bashのtrapがSIGTERMを拾えない?top > 2019 > 1212_trapbashの機能の1つにtrapというものがあります。これは割込みのシグナルを受け取った時の動作を指定する事ができるものなのですが、先日これを使おうとしたらうまく動かないという事がありました。 SIGTERMを受け取ったら止める †trapでは受け取ったシグナルに応じて書く事ができます。例えば下記はSIGTERMを受信したらプログラムを終了するように書いてみます。 # vi trap_test.sh #!/bin/bash -x trap 'echo IN_TRAP; exit 1' SIGTERM sleep 600 これを実行します。 # ./trap_test.sh + trap 'echo IN_TRAP; exit 1' SIGTERM + sleep 600 この状態でSIGTERMシグナルを送ります。 # ps aux | grep trap_test | grep -v grep root 32293 0.0 0.0 12700 3020 pts/0 S+ 12:46 0:00 /bin/bash -x ./trap_test.sh # kill -SIGTERM 32293 で、スクリプトの方はどうなってるかというと・・・ # ./trap_test.sh + trap 'echo IN_TRAP; exit 1' SIGTERM + sleep 600 うんともすんとも言いません・・・・なぜ・・・・・・ ちなみにSIGTERM以外にSIGINTやUSR1なども試しましたがどのシグナルも同じでした。 trapの設定がない場合 †先ほどのスクリプトのtrapの行をコメントして動かしてみます。 #trap 'echo IN_TRAP; exit 1' SIGTERM この状態で実行して、先ほどと同じようにSIGTERMを送ると # ps aux | grep trap_test | grep -v grep root 32341 0.0 0.0 12700 3060 pts/0 S+ 12:50 0:00 /bin/bash -x ./trap_test.sh # kill 32341 # ./trap_test.sh + sleep 600 Terminated [root@ryzen ~]# 今度は正常に終了しました。 ちなみに本題とは関係ないですが、終了したのはシェルスクリプトのみで、裏ではsleepが動き続けています。 # ps aux | grep sleep root 32342 0.0 0.0 7284 828 pts/0 S 12:50 0:00 sleep 600 ちょっと改良 †色々調べて、原因がわかったのでそれを確認する為にちょっと改良。 これは単に1秒おきにsleepするだけのスクリプトです。 #!/bin/bash trap 'echo IN_TRAP; exit 1' SIGTERM CNT=0 while : do echo "CNT=$CNT" CNT=$((CNT+1)) sleep 1 done 実行します。 # ./trap_test.sh CNT=0 CNT=1 CNT=2 ....(略) で、この状態でSIGTERMを送ってみます。 # ./trap_test.sh CNT=0 (略) CNT=9 IN_TRAP 動いた!!!動いたよ!!!! trapはコマンド実行中の割込みはできない †上記の例から、「trapはコマンド実行中に受け取ったシグナルはそれが終わるまで待たなければ行けない」という事のようです。 sleep 1 ←× ↓ ←〇 sleep 1 ←× ↓ ←〇 〇=trap可能 (以下略) ×=trap不可 バッググラウンドに回してwaitの活用 †プログラムによっては細切れにできないものもあると思います。そんな時はバックグラウンドに回してwaitを活用するという方法もあります。 #!/bin/bash trap 'echo IN_TRAP; exit 1' SIGTERM sleep 600 & wait これなら常時SIGTERMを受け取れます(くどいようですが終了してもsleepは生き続けます) 疑似シグナルEXITを使う †割込みを入れるシグナルが単に終了した時だけで十分な場合疑似シグナルという仕組みを利用するのも手です。trapでは疑似シグナルと言ってシステムのシグナルとは別にtrap独自のシグナルが用意されています。何種類かあるのですが、そのうちEXITというシグナルががあるので紹介します。 このEXITシグナルは「スクリプトが終了された」という意味になります。これは先ほどのSIGTERMなどとは違い、スクリプトから生成されたプロセスの状態に関係なく終了すればこの割込みが発生します。 試しに下記のスクリプトを作ってみます。 # vi trap_test.sh #!/bin/bash trap 'echo IN_TRAP' EXIT sleep 600 実行します。 # ./trap_test.sh SIGTERMなどで終了させます。 # ps aux | grep trap_test | grep -v grep root 414 0.0 0.0 12700 3048 pts/0 S+ 13:34 0:00 /bin/bash ./trap_test.sh # kill -SIGTERM 414 するとスクリプトは終了され、しかもtrapが実行されています。 # ./trap_test.sh IN_TRAP Terminated まとめ †
ネットで検索してる限り、疑似シグナルEXITを使ってる人が多いですねぇ。やる事次第ですが、終了の割込み以外ってあんまり使わないですもんねぇ。。 top > 2019 > 1212_trap |