シェルスクリプトで try-catch-finally
プログラミング言語には、たいていの場合 try-catch-finally
で例外処理と終了処理が実装できます。シェルスクリプトのような複雑な機能を実装するのが難しい言語でも try-catch-finally
を実装する方法をまとめます。
実装方法としては
set -e
でエラー発生時にその時点で終了する設定をするtrap
でcatch
,finally
時に実行する関数を設定
です。
シェルスクリプトではエラーが発生ても後続処理が続く
たいていの高級なプログラミング言語では例外処理をしなければプログラムが終了することがほとんどですが、シェルスクリプトではエラーが発生しても後続の処理が普通に実行されてしまいます。
my_script.sh
#!/bin/bash
# ↓この行でエラー
あああ
echo '最終行: エラーが発生しているのに実行されます。'
上のスクリプトの実行結果は以下のようになります。
$ bash my_script.sh
my_script.sh: line 4: あああ: command not found
最終行: エラーが発生しているのに実行されます。
$ echo "終了ステータス: $?"
終了ステータス: 0
4行目の "あああ" というコマンドを実行しようとして、コマンドが見つからずにエラーになっています。エラーが発生していますが、最終行の echo
が実行されていることがわかります。
また、最終的に echo
が実行されているためスクリプトの終了ステータス $?
を参照すると正常終了の 0
となっていることがわかります。
set -e: エラーがあれば強制終了の設定
set -e
はスクリプト内でエラーがあった(exit status
が 0 以外)場合、その時点でスクリプトを終了(exit
してくれるような設定です。
my_script.sh
#!/bin/bash
# スクリプト内でエラーがあれば exit
set -e
# ↓この行でエラー
あああ
echo '最終行: エラーが発生しているのに実行されます。'
$ bash my_script.sh
my_script.sh: line 7: あああ: command not found
$ echo "終了ステータス: $?"
終了ステータス: 127
set -e
がないときと違い、最終行の echo
が実行されずに終了していることがわかります。また、7行目で「コマンドが見つからなかった」という exit status = 127
となっています。set -e
は exit status
が 0
以外だとエラーとして扱います。
もしスクリプト内で部分的にこの設定を無効化したいときには set +e
を実行すればよいです。再度有効化したいときは set -e
をこう一度実行すれば、任意の処理でこの設定を使うことができます。
trap: エラー処理、終了処理を設定
trap
コマンドは、シェルがシグナルを受信したときに実行されるハンドラーを定義してアクティブ化します。
trap [action] [signal]
で指定のシグナルに対して任意のハンドラ関数を設定できます。
今回は trap
コマンドを使って、エラー発生時のシグナル(ERR
)と処理終了時のシグナル(EXIT
)を捕捉して任意の関数(catch
, finally
)を呼び出せるようにします。
ERR
シグナルは -e
オプションが有効な場合にエラーを検知するシグナルです。
my_script.sh
#!/bin/bash
# スクリプト内でエラーがあれば exit
set -e
# trap でエラー発生時と終了時のシグナルを捕捉
trap catch ERR
trap finally EXIT
function catch {
echo 'catch: エラーをキャッチしました。'
}
function finally {
echo 'finally: 処理を終了します。'
}
# ↓この行でエラー
あああ
echo '最終行: エラーが発生しましたが実行されません。'
実行結果
$ bash my_script.sh
my_script.sh: line 18: あああ: command not found
catch: エラーをキャッチしました。
finally: 処理を終了します。
$ echo "終了ステータス: $?"
終了ステータス: 127
trap
でエラー発生時と処理終了時に定義した関数が実行されていることがわかります。さらにスクリプト全体の exit status
が発生したエラーの内容のままになっていることがわかります。
もちろん正常終了する場合には finally
のみが実行されます。
Ctrl+Cを捕捉する trap
例えば Ctrl+C
が実行されたシグナルを捕捉したい場合は SIGINT
シグナルに対してハンドラを設定します。
#!/bin/bash
# スクリプト内でエラーがあれば exit
set -e
# trap でエラー発生時と終了時のシグナルを捕捉
trap catch ERR
trap finally EXIT
trap catch_ctrl_c SIGINT
function catch {
echo 'catch: エラーをキャッチしました。'
}
function catch_ctrl_c {
echo 'catch_ctrl_c: Ctrl+Cが実行されました。'
}
function finally {
echo 'finally: 処理を終了します。'
}
echo 'Ctrl+C を押下してください。'
sleep 1000
実行結果
$ bash my_script.sh
Ctrl+C を押下してください。
^Ccatch_ctrl_c: Ctrl+Cが実行されました。
catch: エラーをキャッチしました。
finally: 処理を終了します。
$ echo "終了ステータス: $?"
終了ステータス: 130
trap
ではそのほかにもいろいろなシグナルが捕捉できます。trap -l
や trap --help
を確認してみてください。
$ trap -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 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
trap -l
では処理が kill
されたときのシグナルが一覧できます。
以上。
コメントを書く