シェルスクリプトを書いていたところで、set -e
やtrap
の挙動に引っかかってしまいました。
なお、「特定の環境で動けばいい」という前提なので、シバンの段階で#!/bin/bash
と決め打っています。POSIXへの準拠性などは考えません。
関数化したら、意図しない動作に
似たような動作をするシェルスクリプトがあったので、一部を関数にして切り出すことにしました。set -e
とtrap ERR
の組み合わせで、エラー時の終了処理はもともと組んでありました。
# main.sh
source ./functions.sh
failure() {
echo エラー発生!
}
trap failure ERR
set -e
func
echo 正常終了
# functions.sh
func() {
# 処理失敗のシミュレーション
false
echo 'func complete'
}
さて、上のコードを実行すれば、何を表示するでしょうか。意図としては、「エラー発生!」だけになってほしいのです…が、なんと何も表示せず終了してしまいます。
動作確認と狙い通りな形への改造
何が起きたのかと調べてみると、以下のような動作をしていたのでした(manより)。
-
set -e
…関数内まで適用される -
trap ERR
…関数内にはデフォルトでは適用されない
ということで、関数内のエラーではtrap
に入らず即座に終了してしまう、という挙動となっていました。
そして、set -E
とすればtrap ERR
が関数にも適用される、ということでやってみたら、今度はエラーが2回表示されるようになりました。今回の場合、一度trap ERR
に入れば復帰する目がなかったので、そのままtrap
の関数の中でexit
させることで、2回目が来る前に終了させて解決しました。
set -E
failure() {
echo エラー発生!
exit 1
}
教訓
いざという時、man
は役に立つ