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はまだまだ闇が深い...