关于bash:管道命令输出到tee但也保存命令的退出代码

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

本问题已经有最佳答案,请猛点这里访问。

我有一个shell脚本,其中包含一个命令(mvn clean install),用于将输出重定向到日志文件。

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

现在,如果mvn clean install因错误而失败,我希望包装外壳脚本也因该错误而失败。但是,由于我将所有输出通过管道传输到tee,因此我无法访问mvn clean install的返回代码,因此当我随后访问$?时,它总是0(因为tee成功了)。

我尝试让命令将错误输出写入一个单独的文件,然后检查,但是MVN的错误输出总是空的(似乎它只写入stdout)。

如何保留mvn clean install的返回代码,但仍将输出通过管道传输到日志文件?


您可以将pipefailshell选项设置为打开,以获得所需的行为。

从bash参考手册:

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.

例子:

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

要恢复原始管道设置,请执行以下操作:

1
$ set +o pipefail


因为您运行的是bash,所以可以使用它的$pipestatus变量而不是$?

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


您可以运行mvn命令并缓存退出代码…我将使用"false"命令作为示例。

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

这样,您就可以使用状态文件内容做出进一步的决定。

我现在很好奇是否有一种更雄辩的方式来实现这一点。


解决方法(注:在Frederic的解决方案中有一个性能更好的解决方案):

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