管道进入 grep -v 时获取退出代码 [重复]

Posted

技术标签:

【中文标题】管道进入 grep -v 时获取退出代码 [重复]【英文标题】:Get exit code when piping into grep -v [duplicate] 【发布时间】:2016-09-04 17:58:44 【问题描述】:

我有一个这样的脚本:

#!/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 并且仍然可以正确传播状态代码?当然,在现实世界中,这样做的目的是过滤嘈杂命令的输出,同时仍然检测命令是否失败。

【问题讨论】:

不知道你是否可以直接这样做,但你总是可以将输出重定向到一个 tmp 文件,它允许你获取初始 grep 的返回,然后将你的第二个 grep 应用于输出文件内容。 limp_chimp 您有机会测试@hek2mgl 或我为您发布的建议吗?如果它对您有用,请务必发布反馈并考虑接受答案。 不知道为什么错过了这个,但grep -v 是一个用于此示例的非常糟糕的命令。这是因为尽管grep 遵循关于退出状态的约定,但grep -v 不一定。换句话说,使用-v 会反转匹配但not 退出代码。我的经验是 grep -v 在返回不匹配时返回成功。如果没有不匹配返回它返回失败。如果没有匹配项,则返回所有不匹配的行并成功。 ^ 使用grep (GNU grep) 2.24 (Copyright (C) 2016 Free Software Foundation, Inc.),这里是citation 【参考方案1】:

利用set -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

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

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

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

#!/bin/sh

echo "hello"
echo "goodbye"
exit 5

然后运行以下命令:

( set -o pipefail; ./fail.sh | grep -v hello ); echo $?

它应该输出:

goodbye
5

以上说明set -o pipefail 不只是以非零返回码退出,而是逐字转发最后一个非零返回码。

【讨论】:

遗憾的是,pipefail 仅在 Bash 上可用,因此当 sh 是 Dash(例如 Ubuntu)或在某些特定的非 Bash shell 下显式运行代码时,您的代码将失败。也许有更符合 POSIX 的解决方案? 在回答问题时,我已经考虑到问题有 BASH 标签,因此,我预计 OP 正在使用 Bash。我愿意接受其他更符合 POSIX 的建议。【参考方案2】:

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

您可以使用进程替换

grep -v hello <(./fail.sh)

您可以使用bash 特定的$PIPESTATUS 变量:

./fail.sh | grep -v hello
if [ ! "$PIPESTATUS[1]" ] ; then
    echo "Foo bar!"
fi

【讨论】:

echo $PIPESTATUS[1] PIPESTATUS:未定义的变量。 你需要使用现代版本的 bash

以上是关于管道进入 grep -v 时获取退出代码 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

通过sed管道输出但保留退出状态[重复]

有没有办法获取早期管道 Scala 进程 (#|) 的退出代码?

grep -l 在到 xargs 的管道上的行为不符合预期[重复]

如何获取管道中第一个命令的退出状态? [复制]

Linux基础命令(more 管道 grep find)

正则 grep 管道符号 “|” 特殊符号“||”“&&” wc 命令 分隔符号cut 排序sort 去重复uniq 符号 ` `的使用