Posted at

teeでリターンコード拾えない問題

More than 1 year has passed since last update.

ERROR

それはITエンジニアにとっての恐怖の象徴。

漆黒のコンソール画面に浮かび上がるその5文字は

一人のエンジニアの帰宅時間を

いともたやすくえげつない時刻に設定する

しかし、

我々ITエンジニアは知っている

「エラーが出てしまったこと」

よりもずっと怖いことがある

それは

「エラーが出るはずの場所でエラーが出ないこと」

である。


問題のtee

hoge.shでエラーが起きても拾えませんでした。。。

./hoge.sh 2>&1 | tee -a hoge.log

RC=$?
if [ RC -ne 0 ]; then
#エラー処理
fi

#正常だったときの後続処理


するまでも無い解説

tee は受け取った標準入力を、

そのまま標準出力として画面に出しつつ、ファイルにも出すコマンドです。

コンソール画面を確認しつつ、ログファイルにも出力するため

ぼくが所属していたプロジェクトではあらゆる箇所で使われてました。

$?は直前のコマンドのリターンコードを返します。

このbashを書いた人は、

./hoge.sh

のコマンドのリターンコードを見たかったんだと思います。

が、$?がひろうのはあくまで「直前」のコマンドなので、このままでは

「tee」コマンドのリターンコードを拾っちゃいます。

うそのような本当の話ですが、

このような書き方が蔓延した半年後くらいに発見されたので、

広範囲にテストやり直しになったことがありました。死ねばいいのに


解決策1:リダイレクトを使う

コンソールには出なくなるけど、ログにはキッチリ残るし

ちゃんとエラー拾えるようになります。

bashになれないとこの細かい記号が覚えられないので

./hoge.sh >> hoge.log 2>&1

RC=$?
if [ RC -ne 0 ]; then
#エラー処理
fi

#正常だったときの後続処理


解決策2:$PIPESTATUS使う(bash系のみ)

個人的にはこっちが好きです。

#!/bin/bash

./hoge.sh 2>&1 | tee -a hoge.log

RC=${PIPESTATUS[0]}
if [ RC -ne 0 ]; then
#エラー処理
fi

#正常だったときの後続処理

PIPESTATUSは直前のパイプ含むコマンド郡のリターンコードを

配列で返します。

echo ${PIPESTATUS[@]}

とかやると全部表示されるのが分かります。


ぼやき

どうやって単体テストを通したの?

レビュワーは何をレビューしたの?

なんで半年も発見されなかったの?