Stdoutとstderrをファイルにリダイレクト(および追加)すると同時に、ターミナルに表示するには、次のようにします。
command 2>&1 | tee -a file.txt
ただし、終了ステータスの正確な値を取得するようにこれを行う別の方法はありますか?
つまり、$?
をテストする場合、command
の終了ステータスではなく、tee
の終了ステータスを確認したいと思います。
ここで${PIPESTATUS[0]}
の代わりに$?
を使用できることは知っていますが、PIPESTATUS
をチェックする必要のない別の解決策を探しています。
おそらく、PIPESTATUSからの終了値を$?
に入れることができます
command 2>&1 | tee -a file.txt ; ( exit ${PIPESTATUS} )
いくつかのbash
フレーバーがある別の可能性は、pipefail
オプションをオンにすることです。
pipefail
設定されている場合、パイプラインの戻り値は、ゼロ以外のステータスで終了する最後の(右端の)コマンドの値です。パイプライン内のすべてのコマンドが正常に終了した場合はゼロになります。このオプションはデフォルトで無効になっています。
set -o pipefail
...
command 2>&1 | tee -a file.txt || echo "Command (or tee?) failed with status $?"
とはいえ、PIPESTATUS
機能を移植可能に実現する唯一の方法(たとえば、POSIX sh
でも機能する)は少し複雑です。つまり、一時ファイル)が必要です。パイプの終了ステータスを親シェルプロセスに伝播します。
{ command 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a file.txt
if [ "`cat \"/tmp/~pipestatus.$$\"`" -ne 0 ] ; then
...
fi
または、再利用のためにカプセル化する:
log2file() {
LOGFILE="$1" ; shift
{ "$@" 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a "$LOGFILE"
MYPIPESTATUS="`cat \"/tmp/~pipestatus.$$\"`"
rm -f "/tmp/~pipestatus.$$"
return $MYPIPESTATUS
}
log2file file.txt command param1 "param 2" || echo "Command failed with status $?"
または、より一般的にはおそらく:
save_pipe_status() {
STATUS_ID="$1" ; shift
"$@"
echo $? >"/tmp/~pipestatus.$$.$STATUS_ID"
}
get_pipe_status() {
STATUS_ID="$1" ; shift
return `cat "/tmp/~pipestatus.$$.$STATUS_ID"`
}
save_pipe_status my_command_id ./command param1 "param 2" | tee -a file.txt
get_pipe_status my_command_id || echo "Command failed with status $?"
...
rm -f "/tmp/~pipestatus.$$."* # do this in a trap handler, too, to be really clean
プロセス置換を使用する:
command > >( tee -a "$logfile" ) 2>&1
ティーはサブシェルで実行されるので$? コマンドの終了ステータスを保持します。
これを行うには、不可解なPOSIXの方法があります。
exec 4>&1; R=$({ { command1; echo $? >&3 ; } | { command2 >&4; } } 3>&1); exec 4>&-
変数R
を戻り値command1
に設定し、command1
の出力をcommand2
にパイプし、その出力を親シェルの出力にリダイレクトします。