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