在 nomatch 的情况下防止 grep 退出

iag*_*ito 67 grep bash exit error-handling

此脚本不回显“之后”:

#!/bin/bash -e

echo "before"

echo "anything" | grep e # it would if I searched for 'y' instead

echo "after"
exit
Run Code Online (Sandbox Code Playgroud)

如果我删除了-eshebang 行上的选项,它也会如此,但我希望保留它,以便我的脚本在出现错误时停止。我不认为 grep 找不到匹配项是错误。我怎样才能防止它突然退出?

小智 70

echo "anything" | { grep e || true; }
Run Code Online (Sandbox Code Playgroud)

解释:

$ echo "anything" | grep e
### error
$ echo $?
1
$ echo "anything" | { grep e || true; }
### no error
$ echo $?
0
### DopeGhoti's "no-op" version
### (Potentially avoids spawning a process, if `true` is not a builtin):
$ echo "anything" | { grep e || :; }
### no error
$ echo $?
0
Run Code Online (Sandbox Code Playgroud)

“||” 表示“或”。如果命令的第一部分“失败”(意思是“grep e”返回非零退出代码),则“||”之后的部分 被执行,成功并返回零作为退出代码(true总是返回零)。

  • 无效,因为如果第一个命令失败,它将隐藏错误。如果管道中的第一个命令失败,正确的解决方案应该返回非零。 (9认同)
  • 不启动`/bin/true` 的略短版本是: `command || :`(所以在你的情况下,`set -e; grep 'needle' haystack || :`)。 (4认同)
  • @DopeGhoti,“true”是某些 shell 中的内置项(至少在 RHEL 上的“bash 4.3”上) (2认同)

小智 35

一种安全且可选的 grep消息的可靠方法:

echo something | grep e || [[ $? == 1 ]] ## print 'something', $? is 0
echo something | grep x || [[ $? == 1 ]] ## no output, $? is 0
echo something | grep --wrong-arg e || [[ $? == 1 ]] ## stderr output, $? is 1
Run Code Online (Sandbox Code Playgroud)

根据posix 手册,退出代码:

  • 1 表示未选择任何行。
  • > 1 表示错误。

  • 这应该是公认的答案,因为如果 grep 没有找到任何东西,它只会抑制警告退出代码 (1),但它会传递真正的错误(退出代码 > 1)。这里的其他解决方案总是抑制真正的错误,这通常是不好的。 (7认同)
  • 这很好,但有一个问题 - 如果 `echo` 命令以 1 退出,则整行命令将以 0 退出,而不仅仅是 `grep`。可能值得检查这个答案:https://unix.stackexchange.com/a/581991/58450。 (3认同)

roe*_*eer 17

另一种选择是向管道添加另一个命令 - 一个不会失败的命令:

echo "anything" | grep e | cat

因为cat现在是管道中的最后一个命令,它的退出状态cat,没有的grep,将被用来确定管道失败与否。

  • 也许这就是[无用猫奖](http://porkmail.org/era/unix/award.html)的一个例子。如果我需要一个*不会失败的命令*,我宁愿使用`:`(或`true`),如@cyker [answer](https://unix.stackexchange.com/a/330670/383404)中所述除此之外没有任何其他意义。`:` 符号实际上只是 true 的别名(即*不要失败*)。更多信息[此处](/sf/answers/225743731/)。 (2认同)
  • @a.barbieri 并非无用;你不能在管道中使用 : 。如果您将命令与逻辑或链接起来,那么是的,但这不是该解决方案的作用。 (2认同)
  • 如果使用`-o pipefail`,这将不起作用。 (2认同)

Wad*_* M. 12

如果您正在使用 grepset -euo pipefail并且不想在未找到记录的情况下退出(退出代码 = 1),但如果它是另一个退出代码,仍然希望它失败,您可以使用:

#!/bin/bash
set -euo pipefail

echo "anything" | { grep e || test $? = 1; } | { grep e2 || test $? = 1; }
Run Code Online (Sandbox Code Playgroud)

管道内的 grep 只会忽略退出状态 = 1。这样你就不需要失去使用set -euo pipefail.

在这个例子中,我特意包含了两个可能无法说明这个概念的管道 grep。

请参阅 myrdd 在那里的帖子。


Jef*_*ler 7

另外一个选项:

...
set +e
echo "anything" | grep e
set -e
...
Run Code Online (Sandbox Code Playgroud)


Cyk*_*ker 5

解决方案

#!/bin/bash -e

echo "before"

echo "anything" | grep e || : # it would if I searched for 'y' instead

echo "after"
exit
Run Code Online (Sandbox Code Playgroud)

解释

set -e 或者 set -o errexit

如果管道(可能由单个简单命令组成)、列表复合命令(见SHELL GRAMMAR上文)以非零状态退出,则立即退出。如果失败的命令是紧跟在所述命令列表的一部分的壳不退出whileuntil关键字,继该测试的一部分ifelif保留字,在执行任何命令的一部分&&||除命令列表以下的最终 &&||,任何命令在管道中但最后一个,或者如果命令的返回值被反转!. 如果子shell 以外的复合命令由于命令在-e被忽略时失败而返回非零状态,则shell 不会退出。ERR如果设置了一个陷阱,则在 shell 退出之前执行。这个选项适用于shell环境和每个子?单独的 shell 环境(见COMMAND EXECUTION ENVIRONMENT上文),并且可能导致子 shell 在执行子 shell 中的所有命令之前退出。

另外,:是 Bash 中的无效命令。