シェルスクリプトでシグナルハンドラを書く

シェルスクリプトでシグナルをトラップするにはtrapを使います。
最初の引数が実行コマンドで、その後ろにトラップするシグナルを並べます。

#!/bin/sh
trap 'echo exit...' EXIT
trap 'echo trap INT or TERM' INT TERM

echo start
sleep 5
echo end

出力

start
end
exit...
start
(^C押す)
trap INT or TERM
end
exit...

EXITはプロセス終了時に投げられる番号0のシグナルです。
シグナル指定は名前でも番号でもよくて、他のシグナルはtrap -lやkill -lで見れます。

$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL
 5) SIGTRAP	 6) SIGABRT	 7) SIGBUS	 8) SIGFPE
 9) SIGKILL	10) SIGUSR1	11) SIGSEGV	12) SIGUSR2
13) SIGPIPE	14) SIGALRM	15) SIGTERM	16) SIGSTKFLT
...

それでは応用例を3つほど。

HUPシグナルで設定ファイルのリロード

hup_handle.sh

#!/bin/sh
startup () {
  . ./env.sh
}
trap startup HUP
startup

while :;do
  echo $STR
  sleep 2
done

env.sh

STR=AAA

実行

$ ./hup_handle.sh
AAA
AAA
...

別ターミナルで設定ファイルを書き換えてHUPシグナルを飛ばしてやれば、

$ echo STR=BBB > env.sh
$ pkill -HUP hup_handle.sh

設定ファイルがリロードされて出力が変わります。

...
AAA
AAA
BBB
BBB
...

終了処理

例えば終了処理としてテンポラリファイルの削除とメッセージの表示を行うには。

#!/bin/sh

end () {
  rm -f *tmp.$$
  echo bye...
}

trap end EXIT
#trap 'exit 255' 1 2 3 15

date  > date.tmp.$$

echo start
cat date.tmp.$$
sleep 10
echo end

テンポラリファイルに$$(PID)をつけてるのは複数同時に起動されたときにごっちゃにならないようにするためです。

2重起動防止

同じスクリプトを複数同時に起動できないようにするにロックファイルで制御するには。

#!/bin/sh
lockfile=lockfile

if [ -f $lockfile ];then
  echo $0 is locked
  exit 100
fi

touch $lockfile

trap 'rm -f $lockfile' EXIT
#trap 'exit 255' 1 2 3 15

echo start
sleep 10
echo end

既に他で起動されてると怒られます。

$ ./lock.sh 
./lock.sh is locked