Posted at

bashでカッコを駆使して例外処理してみる

More than 1 year has passed since last update.

Bashで少し細かい例外処理やtry-catch文っぽいことをしたかったのでメモ


trapでいいんじゃないの?

trapを使うことで、コマンドがエラー復帰した際や、

Ctrl-Cで強制終了された際に実行される処理を定義できます。

#!/bin/bash

finalize(){
echo "Finalization"
exit 1
}

set -e
trap "finalize" ERR 2

コマンド1
コマンド2
コマンド3
コマンド4

しかし、こういう書き方は以下の欠点があると思ってます。


  • 処理途中のコマンドで、エラー復帰を無視したい場合のコーディングが複雑

  • エラー処理の振る舞いを、処理ごとに変えたい場合にわかりづらくなっていく

1点目に手っ取り早く解決するなら、下記のようなコードを書けば、

いいですが、直感的に理解しづらくなります(と私は感じました)。

#!/bin/bash

finalize(){
echo "Finalization"
exit 1
}

set -e
trap "finalize" ERR 2

コマンド1
コマンド2 || true ## コマンド2が失敗しても、正常復帰させる
コマンド3
コマンド4 || true ## コマンド4が失敗しても、正常復帰させる

2点めについては、下記のように書けばいいと思いますが、

どの行でどのtrapが実行されるのかわかりにくくなっていきそうです。。

set -e

hoge1(){

}
trap "hoge1" ERR

コマンド1
コマンド2
コマンド3
コマンド4

hoge2(){
}
trap "hoge2" ERR

コマンド5
コマンド6
コマンド7
コマンド8


サブシェル()を駆使して、try-catchっぽく書く

というわけで、なんとかならないかな?と思ってたどり着いた方法が以下です。

#!/bin/bash

raise(){
echo "raise exception"
exit 1
}

# try
(
コマンド1 || raise
コマンド2
コマンド3 || raise
コマンド4
) || {
# catch
echo "Finalization"
exit 1
}


解説

()でくくると、その中身はサブシェルとして実行されます。

エラー復帰したら例外処理したいコマンドはexit 1させます。

それが、コマンド1 || raiseの部分です。

()から抜けた後は、親シェルに()のリターンコードが返却されます。

0以外が返却された場合は、||でつなげた、catch部分の処理が動きます。


余談

ちなみに trap "finalize" ERR 2すると、Ctrl-Cで終了した後にfinalizeが2回呼び出されました。Bashはまだまだ闇が深い...