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

Bash 进入grep-v时获取退出代码,bash,grep,exit-code,Bash,Grep,Exit Code,我有这样一个脚本: #!/bin/sh echo "hello" echo "goodbye" exit 1 当我自己运行它时,我得到了预期的失败退出代码 $ ./fail.sh hello goodbye $ echo $? 1 但是,当我通过grep-v运行它时,退出状态变为成功: $ ./fail.sh | grep -v hello goodbye $ echo $? 0 是否有办法将命令的输出导入grep-v,并且仍然正确传播状态代码?当然,在现实世界中,这一点将是过滤嘈杂命令

我有这样一个脚本:

#!/bin/sh

echo "hello"
echo "goodbye"
exit 1
当我自己运行它时,我得到了预期的失败退出代码

$ ./fail.sh
hello
goodbye
$ echo $?
1
但是,当我通过
grep-v
运行它时,退出状态变为成功:

$ ./fail.sh | grep -v hello
goodbye
$ echo $?
0

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

利用
设置-o pipefail
,以下操作应该可以工作:

( set -o pipefail; ./fail.sh | grep -v hello )
然后可以在
$?
中测试该值:

( set -o pipefail; ./fail.sh | grep -v hello ); if [[ "$?" -eq "1" ]]; then echo success; else echo bummer; fi 
它应该输出:

goodbye
success
goodbye
5

发生了什么以及为什么会这样做?

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

您可以通过将脚本更新为以下内容来测试这一点:

#!/bin/sh

echo "hello"
echo "goodbye"
exit 5
然后运行以下命令:

( set -o pipefail; ./fail.sh | grep -v hello ); echo $?
它应该输出:

goodbye
success
goodbye
5

上面说明了
set-o pipefail
不仅仅是带着非零返回代码退出,而是逐字转发最后一个非零返回代码。

bash
基本上只会给出管道中第一个进程的退出代码。您有两个选择:

您可以使用流程替换:


不知道您是否可以直接这样做,但您始终可以将输出重定向到tmp文件,这样您就可以获取初始grep的返回,然后将第二个grep应用到输出文件内容。limp_chimp您是否有机会测试@hek2mgl或我为您发布的建议?如果它对你有用,一定要发布反馈并考虑接受答案。不知道为什么会错过这个,但是<代码> GRIP-V是这个例子中使用的一个非常糟糕的命令。这是因为尽管
grep
在退出状态方面遵循惯例,但
grep-v
不一定。换句话说,使用
-v
反转匹配,但不反转退出代码。我的经验是,
grep-v
在返回不匹配项时返回success。如果没有要返回的非匹配项,则返回fail。如果没有匹配,它返回所有不匹配和成功的行。^使用<代码> GRIP(GNU-GRIP)2.24 < /代码>(<代码>版权(C)2016免费软件基金会,Inc. <代码>),这里是一个回音${PIPESTATION(1)} PIPESTATION:未定义的变量。您需要使用BasHSADLY的现代版本<代码> PIPELVECU/<代码>仅在BASH上可用,因此,如果
sh
是Dash(例如Ubuntu),或者在特定的非Bash shell下显式运行代码,那么代码就会失败。也许有一个更符合POSIX的解决方案?在回答这个问题时,我考虑到这个问题带有BASH标签,因此,我认为OP使用的是BASH。我愿意接受其他更让POSIX高兴的建议。