我有一些带有 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
,我说你观察到的行为绝对不是grep
shell 的错误。该错误在您的脚本中。
我在链接问题下的回答提供了解决方案。对你来说最简单的一种是:
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
?因为你的echo
shell 很可能是内置的,当“它”收到 SIGPIPE 时,那么它实际上是整个子 shell 收到 SIGPIPE 并被杀死。我们不希望应该执行的子 shelltrue
被杀死。见开头这个答案。
检查整个脚本;其他条件也可能以同样的方式出现问题。