Pipe output and capture exit status in Bash

Viewed 194147

I want to execute a long running command in Bash, and both capture its exit status, and tee its output.

So I do this:

command | tee out.txt
ST=$?

The problem is that the variable ST captures the exit status of tee and not of command. How can I solve this?

Note that command is long running and redirecting the output to a file to view it later is not a good solution for me.

15 Answers

There is an internal Bash variable called $PIPESTATUS; it’s an array that holds the exit status of each command in your last foreground pipeline of commands.

<command> | tee out.txt ; test ${PIPESTATUS[0]} -eq 0

Or another alternative which also works with other shells (like zsh) would be to enable pipefail:

set -o pipefail
...

The first option does not work with zsh due to a little bit different syntax.

Dumb solution: Connecting them through a named pipe (mkfifo). Then the command can be run second.

 mkfifo pipe
 tee out.txt < pipe &
 command > pipe
 echo $?

using bash's set -o pipefail is helpful

pipefail: the return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if no command exited with a non-zero status

There's an array that gives you the exit status of each command in a pipe.

$ cat x| sed 's///'
cat: x: No such file or directory
$ echo $?
0
$ cat x| sed 's///'
cat: x: No such file or directory
$ echo ${PIPESTATUS[*]}
1 0
$ touch x
$ cat x| sed 's'
sed: 1: "s": substitute pattern can not be delimited by newline or backslash
$ echo ${PIPESTATUS[*]}
0 1
(command | tee out.txt; exit ${PIPESTATUS[0]})

Unlike @cODAR's answer this returns the original exit code of the first command and not only 0 for success and 127 for failure. But as @Chaoran pointed out you can just call ${PIPESTATUS[0]}. It is important however that all is put into brackets.

Related