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

Ing*_*her 150 bash shell pipe sh tee

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

#!/bin/bash
...
mvn clean install $@ | tee $logfile
echo $? # Does not show the return code of mvn clean install
Run Code Online (Sandbox Code Playgroud)

现在,如果mvn clean install失败并出现错误,我希望我的包装器shell脚本也会因该错误而失败.但是因为我将所有输出都传递给tee,所以我无法访问返回代码mvn clean install,所以当我$?之后访问时,它总是0(因为tee成功).

我尝试让命令将错误输出写入单独的文件并在之后检查,但mvn的错误输出始终为空(看起来它只写入stdout).

如何保留返回代码mvn clean install但仍然将输出传递给日志文件?

Juk*_*nen 194

您可以设置pipefail shell选项选项以获取所需的行为.

Bash参考手册:

除非pipefail启用该选项,否则管道的退出状态是管道中最后一个命令的退出状态(请参阅Set Builtin).如果pipefail启用,则管道的返回状态是以非零状态退出的最后(最右侧)命令的值,如果所有命令都成功退出,则返回零.

例:

$ false | tee /dev/null ; echo $?
0
$ set -o pipefail
$ false | tee /dev/null ; echo $?
1
Run Code Online (Sandbox Code Playgroud)

要恢复原始管道设置:

$ set +o pipefail
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是比公认的解决方案更优雅的解决方案 (27认同)
  • 或者在PIPESTATUS上做一个循环 (5认同)
  • 恕我直言,这应该是正确的答案:“(set -o pipefail && false | tee log.txt)”在子 shell 中执行这些内容,以便恢复 pipelinefail 标志的原始状态 (3认同)
  • 同意 接受的答案要求您事先知道管道中的哪个命令将失败。如果将5条不同的命令组合在一起,则必须猜测阵列中的哪一条将失败。 (2认同)
  • 恢复原始管道设置:`$ set +o pipefail` (2认同)

Fré*_*idi 143

由于您正在运行bash,您可以使用其$ PIPESTATUS变量而不是$?:

mvn clean install $@ | tee $logfile
echo ${PIPESTATUS[0]}
Run Code Online (Sandbox Code Playgroud)

  • 让我的一天:)) (5认同)
  • 同样非常重要的是:该变量是短暂的,因此即使“回显”它也会失去运行管道的价值。将其分配给另一个变量,除非您将立即访问它,并且仅访问一次。 (3认同)
  • 如下所述,如果您有多个管道,则需要检查每个命令的状态以了解失败的地方。 (2认同)

Dem*_*nex 11

您可以运行mvn命令并缓存退出代码...我的示例使用"false"命令.

$ { false ; echo $? > /tmp/false.status ; } | tee $logfile
$ cat /tmp/false.status
1
Run Code Online (Sandbox Code Playgroud)

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

我现在很好奇是否有更有说服力的方法来实现这一目标.