grep --quiet 的退出状态有错误吗?

grep --quiet 的退出状态有错误吗?

我有一些带有 grep 版本 2.16 的 SLES 12 SP5 机器,并且在一台机器上我大量使用包含以下grep --quiet条件的脚本:

# $pid_list contains the result of pstree and $script_pid equals $$
if echo "$pid_list" | grep -qF "($script_pid)"; then
  continue
fi
if echo "$pid_list" | grep -qF "($script_pid)"; then
  echo "Error: grep has a bug!"
  continue
fi

我将其加倍,因为第一个条件失败的概率约为 0.1%,而第二个相同条件成功?!

按如下方式更改条件后,它可以完美运行(完整代码这里):

if echo "$pid_list" | grep -F "($script_pid)" >/dev/null; then
  continue
fi

关于手册,该quiet选项的行为应该符合我的预期。如果发生错误,它甚至应该返回 true:

如果发现任何匹配,即使检测到错误,也会立即以零状态退出

所以我很困惑为什么有时会失败。机器的 RAM 和文件系统都很好。 grep 二进制文件也具有正确的文件哈希值。

搜索提交,但我唯一找到的是从2001年开始,这应该是一部分2.16 从 2014 年开始

更新1

我尝试按照@kamil的建议使用子shell,但它仍然失败(有时会显示“竞争条件”错误):

if (echo "$pid_list"; true) | grep -qF "($script_pid)"; then
  continue
elif (echo "$pid_list"; true) | grep -qF "($script_pid)"; then
  echo "Error: Race condition!"
  continue
fi

相反,这有效:

if echo "$pid_list" | grep -qF "($script_pid)" || [[ $? -eq 141 ]]; then
  continue
fi

答案1

假设:在您执行的脚本中set -o pipefail(如果解释脚本的 shell 是 Bash;其他 shell 可能提供类似的功能)。从man 1 bash

pipefail
如果设置,管道的返回值是最后一个(最右边)以非零状态退出的命令的值,如果管道中的所有命令都成功退出,则返回值为零。默认情况下禁用此选项。

当有匹配时,grep -q提前退出并返回0。然后echo 可能会或可能不会返回141。这取决于在关闭管道末端echo之前是否设法将所有内容写入管道缓冲区。grep即使对于相同的输入,事情也可能会以一种方式或另一种方式发展,这是一种竞争条件。如果141发生这种情况,那么管道的返回值将是141因为pipefail

假设你真的使用过set -o pipefail,我说你观察到的行为绝对不是grepshell 的错误。该错误在您的脚本中。

我在链接问题下的回答提供了解决方案。对你来说最简单的一种是:

if (/bin/echo "$pid_list"; true) | grep -qF "($script_pid)"; then

或者

if ((echo "$pid_list"); true) | grep -qF "($script_pid)"; then

为什么/bin/echo …(echo …)代替echo?因为你的echoshell 很可能是内置的,当“它”收到 SIGPIPE 时,那么它实际上是整个子 shell 收到 SIGPIPE 并被杀死。我们不希望应该执行的子 shelltrue被杀死。见开头这个答案

检查整个脚本;其他条件也可能以同样的方式出现问题。

相关内容