To redirect (and append) stdout and stderr to a file, while also displaying it on the terminal, I do this:
command 2>&1 | tee -a file.txt However, is there another way to do this such that I get an accurate value for the exit status?
That is, if I test $?, I want to see the exit status of command, not the exit status of tee.
I know that I can use ${PIPESTATUS[0]} here instead of $?, but I am looking for another solution that would not involve having to check PIPESTATUS.
Understanding the concept of redirections and file descriptors is very important when working on the command line. To redirect stderr and stdout , use the 2>&1 or &> constructs.
Perhaps you could put the exit value from PIPESTATUS into $?
command 2>&1 | tee -a file.txt ; ( exit ${PIPESTATUS} )
Another possibility, with some bash flavours, is to turn on the pipefail option:
pipefail
If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully. This option is disabled by default.
set -o pipefail ... command 2>&1 | tee -a file.txt || echo "Command (or tee?) failed with status $?" This having been said, the only way of achieving PIPESTATUS functionality portably (e.g. so it'd also work with POSIX sh) is a bit convoluted, i.e. it requires a temp file to propagate a pipe exit status back to the parent shell process:
{ command 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a file.txt if [ "`cat \"/tmp/~pipestatus.$$\"`" -ne 0 ] ; then ... fi or, encapsulating for reuse:
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 $?" or, more generically perhaps:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With