LoginSignup
21
16

More than 5 years have passed since last update.

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

Posted at

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

21
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
16