以下の投稿、根本的な間違いがあったのであとで直します。(shと思ってたものが、実はbashだったとは、、)
目的
シェルスクリプトを書いていて終了処理を共通化したいことがあります。
オープンしたリソースを閉じたり、一時ファイルを削除したり、exitの前に一つずつ書くのは結構面倒です。そんなときにはtrapを使用しましょう。(小ネタですが、実用度は高いですね。)
コード
#!/bin/sh
end(){echo end !!;}
trap end exit
echo main !!
実行結果
[sample@hostname ~]$ ./trap_sample.sh
main !!
end !!
[sample@hostname ~]$
説明
trapを使用すれば各種シグナルが発生した場合に関数を呼び出すことが可能です。
この例ではexitシグナル発生時にend関数を呼び出す設定をしています。これを使用すれば長くなりがちな異常時処理を簡潔に書けるようになるのではないでしょうか。
ただし、一点注意が必要なのは各シグナルに対してのtrap設定は1個までしか持つことができないので、複雑な終了処理を実装するには工夫してやる必要があります。
応用
trapの応用例として、関数のスタックを表示する例を考えました。
シェルスクリプトが異常終了した場合に「どこで落ちたかわからない。」みたいなことがあると困るのでtrapとFUNCNAMEを組み合わせてスタックを表示します。
コード
#!/bin/sh
trace()
{
local rc=$?
echo -n [Stack Info] 1>&2
echo ${FUNCNAME[@]}|awk 'BEGIN{OFS=" <-"}{$1="exit";print }' 1>&2
exit $rc
}
trap trace exit
sub1(){ sub2;}
sub2(){ sub3;}
sub3(){ echo exit!!; exit 1;}
sub1
実行結果
[sample@hostname ~]$ ./trace_on_exit.sh
exit!!
[Stack Info]exit <-sub3 <-sub2 <-sub1 <-main
[sample@hostname ~]$
説明
FUNCNAMEは配列型の環境変数で、第一要素には現在実行中の関数名、第二要素にはそれを呼び出した関数名、第三要素には、、、、といった具合にスタック情報が入っています。
ここではtrap関数と組み合わせることで終了時にスタック情報を出力しています。
(BASH_SOURCE、BASH_LINENOを使用してソース名や実行行数まで出力できそうなのですが、trapの場合は特殊なのかうまくいかず、、。とりあえず関数名だけの出力で良しとします。)