管道进入 grep -v 时获取退出代码

lim*_*imp 4 bash grep exit-code

我有一个这样的脚本:

#!/bin/sh

echo "hello"
echo "goodbye"
exit 1
Run Code Online (Sandbox Code Playgroud)

当我自己运行它时,我得到了我预期的失败退出代码。

$ ./fail.sh
hello
goodbye
$ echo $?
1
Run Code Online (Sandbox Code Playgroud)

但是,当我运行它时grep -v,退出状态更改为成功:

$ ./fail.sh | grep -v hello
goodbye
$ echo $?
0
Run Code Online (Sandbox Code Playgroud)

有没有办法将命令的输出通过管道输入grep -v并仍然正确传播状态代码?当然,在现实世界中,这样做的目的是过滤嘈杂命令的输出,同时仍然检测命令是否失败。

Joh*_*ell 7

利用set -o pipefail,以下应该起作用:

( set -o pipefail; ./fail.sh | grep -v hello )
Run Code Online (Sandbox Code Playgroud)

然后您可以测试中的值$?

( set -o pipefail; ./fail.sh | grep -v hello ); if [[ "$?" -eq "1" ]]; then echo success; else echo bummer; fi 
Run Code Online (Sandbox Code Playgroud)

它应该输出:

goodbye
success
Run Code Online (Sandbox Code Playgroud)

发生了什么,为什么会这样?

如 OP 中所述,如果最后一个命令出错,管道通常只返回失败(非零返回代码)。如果set -o pipefail管道中的任何命令出错,则使用会导致命令管道产生失败返回码。管道传递的失败返回码是最后一条失败命令的返回码。

您可以通过将脚本更新为:

#!/bin/sh

echo "hello"
echo "goodbye"
exit 5
Run Code Online (Sandbox Code Playgroud)

然后运行以下命令:

( set -o pipefail; ./fail.sh | grep -v hello ); echo $?
Run Code Online (Sandbox Code Playgroud)

它应该输出:

goodbye
5
Run Code Online (Sandbox Code Playgroud)

上面说明了这set -o pipefail不仅仅是以非零返回代码退出,而是逐字传递最后一个非零返回代码。

  • 遗憾的是`pipefail` 只在Bash 上可用,所以你的代码在`sh` 是Dash(例如Ubuntu)或者你在某些特定的非Bash shell 下显式运行你的代码时会失败。也许有更符合 POSIX 的解决方案? (2认同)