Posted at
zshDay 19

zsh で 例外処理

More than 5 years have passed since last update.

zsh はプログラム言語で言うところの try/catch のような例外処理ができるので紹介する。


シンプルな例

書き方はこんな感じ。

#!/bin/zsh

autoload -Uz catch
autoload -Uz throw

{
echo hoge
throw 'MyException'
echo fuga
} always {
if catch 'MyException'; then
echo "catch MyException!"
fi
}

例外を投げるには throw、受け取るには catch という関数を使う。まずは両方とも autoload しておく。

例外処理をするときは全体を

{

# try ブロック
# ここにメイン処理を書く
} always {
# always ブロック
# 例外が発生した時もそうでない時も実行される
}

という形で書く。

always ブロックは例外が発生したかどうかにかかわらず常に実行されるので、この中で catch を使って例外が発生したかどうか判定すれば例外をキャッチできる。

この例を実行した結果はこんな感じ。

% ./exception_sample.zsh

hoge
catch MyException!

例外を投げた時点でうまく always ブロックに移行してるのが分かる。


複数の例外の種類を考慮する例

もうちょっと複雑な例を見てみる。

#!/bin/zsh

autoload -Uz catch
autoload -Uz throw

{
lock_dir_name="lock_dir"

mkdir $lock_dir_name || throw 'LockException'

# メイン処理
# ...

rmdir "$lock_dir_name"
} always {
if catch '*'; then
case
$CAUGHT in
(LockException)
echo "LockException"
;;
(NetworkException)
echo "NetworkException"
;;
(*)
echo "other Exception"
;;
esac
fi

# 後処理が必要ならここに書く
# 例外が発生するかどうかにかかわらず実行される
# 他の言語で言う finally に相当する

echo "tear down"
}

このスクリプトでは、ディレクトリを作って排他制御をしてる。ロックの取得に失敗したときはすぐに例外を投げてメイン処理を行わないようになってる。

この例のように catch の引数はワイルドカードが使えるので、catch '*' とすればどんな名前の例外でもキャッチできる。どんな例外もキャッチして、例外の種類ごとに違うことをしたい場合はこんなふうに書くとうまくいく。

その他詳しいことは man zshcontrib の EXCEPTION HANDLING や man zshmisc の COMPLEX COMMANDS の { try-list } always { always-list } を参照。

こんな感じで例外処理を使うとエラー処理がシンプルに書けるようになるので、ぜひ試してみてください!