シェルスクリプトでtry-catch-finallyを実装する方法

シェルスクリプトでtry-catch-finallyを実装する方法

シェルスクリプトで try-catch-finally

プログラミング言語には、たいていの場合 try-catch-finally で例外処理と終了処理が実装できます。シェルスクリプトのような複雑な機能を実装するのが難しい言語でも try-catch-finally を実装する方法をまとめます。

実装方法としては

  1. set -e でエラー発生時にその時点で終了する設定をする
  2. trapcatch, 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 となっていることがわかります。

終了ステータス – Wikipedia

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 -eexit status0 以外だとエラーとして扱います。

もしスクリプト内で部分的にこの設定を無効化したいときには 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 -ltrap --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 されたときのシグナルが一覧できます。

以上。

参考URL

Linuxカテゴリの最新記事