Piping command output to tee but also save exit code of command

BashShellPipeShTee

Bash Problem Overview


I have a shell script in which I wrap a command (mvn clean install), to redirect the output to a logfile.

#!/bin/bash
...
mvn clean install $@ | tee $logfile
echo $? # Does not show the return code of mvn clean install

Now if mvn clean install fails with an error, I want my wrapper shell script also fail with that error. But since I'm piping all the output to tee, I cannot access the return code of mvn clean install, so when I access $? afterwards, it's always 0 (since tee successes).

I tried letting the command write the error output to a separate file and checking that afterwards, but the error output of mvn is always empty (seems like it only writes to stdout).

How can I preserve the return code of mvn clean install but still piping the output to a logfile?

Bash Solutions


Solution 1 - Bash

You can set the pipefail shell option option on to get the behavior you want.

From the Bash Reference Manual:

> The exit status of a pipeline is the exit status of the last command > in the pipeline, unless the pipefail option is enabled (see The Set Builtin). > If pipefail is enabled, the pipeline's return status is the > value of the last (rightmost) command to exit with a non-zero status, > or zero if all commands exit successfully.

Example:

$ false | tee /dev/null ; echo $?
0
$ set -o pipefail
$ false | tee /dev/null ; echo $?
1

To restore the original pipe setting:

$ set +o pipefail

Solution 2 - Bash

Since you're running bash, you can use its $PIPESTATUS variable instead of $?:

mvn clean install $@ | tee $logfile
echo ${PIPESTATUS[0]}

Solution 3 - Bash

You could run the mvn command and cache the exit code... I use the "false" command for my example.

$ { false ; echo $? > /tmp/false.status ; } | tee $logfile
$ cat /tmp/false.status
1

That way you can use the status file content to make further decisions.

I'm curious now whether there is a more eloquent way to accomplish this.

Solution 4 - Bash

Workaround (note: a perfer @Frederic's solution):

f=`mktemp`
(mvn clean install $@; echo $?>$f) | tee $logfile
e=`cat $f` #error in variable e
rm $f

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWolkenarchitektView Question on Stackoverflow
Solution 1 - BashJukka MatilainenView Answer on Stackoverflow
Solution 2 - BashFrédéric HamidiView Answer on Stackoverflow
Solution 3 - BashDemosthenexView Answer on Stackoverflow
Solution 4 - BashKaroly HorvathView Answer on Stackoverflow