今回実現すること
シェルスクリプトの途中でエラーが発生した(コマンドの終了ステータスが 0 以外だった)とき、スクリプトを中断し、エラー発生時の環境情報を出力する。実行例は以下。
$ ./sample1.sh aa "bb" "cc dd"
hello
mondai hassei ← ここで問題発生
------------------------------------------------------------
Error occured on ./sample1.sh [Line 8]: Status 1
PID: 3888
User: kobake
Current directory: /test/shell
Command line: ./sample1.sh "aa" "bb" "cc dd"
------------------------------------------------------------
手段概要
シェルスクリプトの実行において set -e
しておくと、それ以降のコマンド実行結果が正常でなかった(終了ステータスが 0 以外だった)場合にスクリプトが中断するわけだが、ただ何もせずに終了してしまうと問題解析がめんどくさい。
そんなわけで set -e
する際には trap
を利用して何かしらのメッセージを表示するのが一般的かと思う。
ただ、シェルスクリプト毎に trap 処理を書くのは非生産的なので、共通の trap 処理があるとなかなか捗る。
汎用トラップコード
エラーを検出したときに onerror が呼ばれるようにしているのだが、この中で問題解析時によく参照するであろう諸々の情報を出力しておく、というもの。
#!/bin/bash
onerror()
{
status=$?
script=$0
line=$1
shift
args=
for i in "$@"; do
args+="\"$i\" "
done
echo ""
echo "------------------------------------------------------------"
echo "Error occured on $script [Line $line]: Status $status"
echo ""
echo "PID: $$"
echo "User: $USER"
echo "Current directory: $PWD"
echo "Command line: $script $args"
echo "------------------------------------------------------------"
echo ""
}
begintrap()
{
set -e
trap 'onerror $LINENO "$@"' ERR
}
begintrap
トラップコードの利用
./error_trap.sh を「.」により実行することで、エラー監視が始まる。
#!/bin/bash
# エラートラップ開始
. ./error_trap.sh
# 処理
echo hello
./e.sh
echo world
#!/bin/bash
>&2 echo "mondai hassei"
exit 1
e.sh はエラーを返すだけのダミースクリプト。
echo hello
./e.sh ← ここでエラーが発生
echo world
実行結果
こんな感じ。
hello 出力の後の e.sh でエラーが発生し、そこで処理は中断される。それ以降の world 出力は実行されない。
$ ./sample1.sh aa "bb" "cc dd"
hello
mondai hassei
------------------------------------------------------------
Error occured on ./sample1.sh [Line 8]: Status 1
PID: 3888
User: kobake
Current directory: /test/shell
Command line: ./sample1.sh "aa" "bb" "cc dd"
------------------------------------------------------------
必要に応じて onerror には $PATH
出力等を加えたり等、諸々情報を増やしていくと良い(邪魔にならない範囲で)。
スクリプトの出力内容が多いとパっと見でエラー情報が埋もれがちなので、こうやって罫線で囲って目立つようにしてあげるのもけっこう大事だと思っている。