bash でのシグナルトラップ
例外からの復帰
他のプログラミング言語でシグナリングを使用する場合と同様に、シグナル トラップは間違った方法で簡単に実行できます。
使用する場合、トラップ評価内で関数を処理するtrap
必要はありませんが、例外の実行をできるだけ短くするために、トラップ例外の終了後にメインプログラムがチェックできるフラグのみを設定する必要があります。
特に、トラップ実行レベルでサブプロセスを開始する必要はありません!fork
正しい例
#!/bin/bash
SERVER_PID=""
CMD_TRAP=""
npm() { #Doing something that could be checked from external
if [ "$1" ] && [ "$1" == "start" ] ;then
while :;do
date "+%s%N" >/tmp/dummyfile.txt
sleep .333
done
fi
}
start_server() {
npm start &
SERVER_PID=$!
}
terminate_server() {
[ "$SERVER_PID" ] && ps $SERVER_PID &>/dev/null && kill -TERM $SERVER_PID
SERVER_PID=""
}
refresh_server() {
terminate_server
start_server
}
printf "for:\n server restart, hit: 'kill -USR2 %d'\n" $$
printf " server stop, hit: 'kill %d' (or Ctrl+C)\n" $$
trap 'CMD_TRAP=refresh' USR2 HUP
trap 'CMD_TRAP=terminate' TERM INT
start_server
while [ "$SERVER_PID" ];do
wait $SERVER_PID
case "$CMD_TRAP" in
refresh ) refresh_server ;;
terminate ) terminate_server ;;
* ) refresh_server ;; # in case server just end.
esac;
CMD_TRAP=""
[ "$SERVER_PID" ] && echo "LOOP." || echo "EXIT."
done
特徴
このデモ スクリプトは次のことを行います。
- スクリプトの開始時にサービスが開始されます。
USR2
またはHUP
信号が受信されると、サービスが再開されます。
- サービスが終了するか、信号を受信した場合にサービスが再開され、
TERM
信号が受信されるか
、サービスが適切に停止されます
- 例外は正しく処理されます (すぐにメインルーチンに戻ります)
- 不要な/管理されていないエラー メッセージがない
出力サンプル
ウィンドウ1
tty
/dev/pts/0
ウィンドウ2
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S+ 0:00 \_ bash
ウィンドウ1
./serverScript.sh
for:
server restart, hit: 'kill -USR2 11469'
server stop, hit: 'kill 11469' (or Ctrl+C)
ウィンドウ2
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S 0:00 \_ bash
11469 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
11470 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
cat /tmp/dummyfile.txt
1361603642256133674
cat /tmp/dummyfile.txt
1361603648712606114
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S 0:00 \_ bash
11469 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
11470 pts/0 S+ 0:01 \_ /bin/bash ./servermon.sh
16814 pts/0 S+ 0:00 \_ sleep .333
kill -USR2 11469
ウィンドウ1
LOOP.
ウィンドウ2
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S 0:00 \_ bash
11469 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
17152 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
17532 pts/0 S+ 0:00 \_ sleep .333
cat /tmp/dummyfile.txt
1361604208069564188
cat /tmp/dummyfile.txt
1361604209103660589
kill -USR2 11469
ウィンドウ1
LOOP.
ウィンドウ2
cat /tmp/dummyfile.txt
1361604278583723517
cat /tmp/dummyfile.txt
1361604279605292149
kill 11469
ウィンドウ1
EXIT.
$
ウィンドウ 1 は、ループとサーバー サブプロセスを終了します。
ウィンドウ1
./serverScript.sh
for:
server restart, hit: 'kill -USR2 19232'
server stop, hit: 'kill 19232' (or Ctrl+C)
次にCtrl+Cが押された場合:
ウィンドウ1
^CEXIT.
$
ウィンドウ2
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S+ 0:00 \_ bash
説明
例外をスタックできませんでした。したがって、例外が実行されている間、別の割り込みを無視できます。
refresh_server()
これは、サーバーを再起動する前に、関数がいくつかのログを圧縮してローテーションする必要があるサンプルの場合に重要になる可能性があります。
refresh_server() {
terminate_server
lockedfilename=-$(date +%F_%H-%M-%S-$$)
mv /srv/logfile /srv/logfile-$lockedfilename
gzip /srv/logfile-$lockedfilename
start_server
}
多くの割り込みは、メイン ループで要約または無視できますが、処理はメイン レベルでのみ実行する必要があります。
何が悪いのかについての小さなデモがあります:
ウィンドウ1
trap "echo USR2 sleep 4;sleep 4" USR2
while :;do printf "\r%s " $(date +%s%N);sleep .333;done
1361606565xxxxxxxxx
(xxxxxxxxx
変化 3x / 秒)
ウィンドウ2
for ((i=5;i--;));do echo KILL;kill -USR2 5187;sleep .5;done
KILL
KILL
KILL
KILL
KILL
1361606722582175969 USR2 sleep 4
USR2 sleep 4
1361606770xxxxxxxxx
ループ全体でトラップされたのは、5 回中 2 回の割り込みだけでした。
説明を検索すると: 5 x 0.5 = 2.5 秒。スリープ時間は 4 秒よりはるかに短いのに、5 回すべてではなく 2 回目の割り込みを受け取ったのはなぜですか??
メインループでアントラップ
メインループでトラップを解除するためのちょっとしたトリックがあります:
while [ "$SERVER_PID" ];do
wait $SERVER_PID
OLDIFS="$IFS" IFS=$'\n'
TRAPS=($(trap)) # save traps
IFS="$OLDIFS"
eval "$(printf "trap -- %s\n" ${TRAPS[@]##*\'})" # untrap
case "$CMD_TRAP" in
refresh ) refresh_server ;;
terminate ) terminate_server ;;
* ) refresh_server ;; # in case server just end.
esac;
CMD_TRAP=""
if [ "$SERVER_PID" ]
then echo "LOOP."
else echo "EXIT."
eval "$(printf "%s\n" "${TRAPS[@]}")" # restore traps
fi
done