bash shell命令行中参数“-e”的含义是什么?

114

我有一个带有头部#!/bin/bash -e的Bash Shell脚本。

当我运行脚本时,grep命令运行后会被中断,但是当我删除参数-e后,脚本可以正常运行。 参数-e的含义是什么?


1
@MitchWheat 但是 bash 没有 -e 选项 (http://linux.die.net/man/1/bash),所以在这种情况下到底发生了什么? :( - user166390
5
确实不太明显,但是bash确实有一个“-e”选项:http://serverfault.com/questions/391255/what-does-passing-the-xe-parameters-to-bin-bash-do。 - Eric O. Lebigot
只有一个小细节需要注意。例如,当你遇到这个:[-e filepath] 如果文件存在则返回真。 - Alberto Perez
2个回答

149
-e选项的意思是“如果任何管道以非零('错误')退出状态结束,则立即终止脚本”。由于当grep没有找到任何匹配时,它会返回1的退出状态,因此即使没有真正的“错误”,它也可能导致-e终止脚本。
如果您想保留-e选项,但也要有一个grep命令,该命令可能有效地找不到任何匹配项,您可以在grep命令后附加|| :。这意味着“或者,如果grep命令返回非零退出状态,则运行:(什么也不做)”; 因此,净效果是禁用-e选项对于grep命令。所以:
grep PATTERN FILE... || :

编辑备注: 以上方法会忽略每一个错误: 如果grep返回1,表示找不到匹配的内容,则会被忽略;如果grep返回2,表示出现了错误,则也会被忽略;如果grep不在路径中(因此Bash返回127),则同样被忽略 - 等等。因此,最好使用一个检查结果代码并在其它情况下重新发出错误的命令,而不是:。例如:

grep PATTERN FILE || (( $? == 1 ))

但这样会破坏退出状态;通常,当一个失败的命令以-e终止Bash脚本时,脚本将返回命令的退出状态,但在上面的示例中,脚本只会返回1。如果我们关心这个问题(且只有这种情况下),我们可以编写如下内容来解决它:

grep PATTERN FILE || exit_code=$?
if (( exit_code > 1 )) ; then
    exit $exit_code
fi

此时,最好创建一个shell函数来帮助我们处理:

(第一行由dsummersl的评论提供)

function grep_no_match_ok () {
    local exit_code
    grep "$@" || exit_code=$?
    return $(( exit_code == 1 ? 0 : exit_code ))
}

(注意使用return而不是exit;我们将让-e在适当时退出处理);这样,我们就可以只写:)
grep_no_match_ok PATTERN FILE     # won't kill script if no matches are found

事实上,由于我们很可能希望在此脚本中使用此函数来处理grep所有出现情况,因此我们实际上可以将该函数命名为grep

function grep () {
    local exit_code
    command grep "$@" || exit_code=$?
    return $(( exit_code == 1 ? 0 : exit_code ))
}

grep PATTERN FILE     # won't kill script if no matches are found

请注意在其自身主体内使用command来绕过shell函数的使用。我们希望该函数调用常规程序grep,而不是无限递归。


非常好。我开始采用您的建议来捕获退出代码以供以后使用:grep XXX FILE || exitcode=$?。超级方便! - dsummersl
@dsummersl:很酷,我很高兴你喜欢它!你的评论激发了我加入一些可能会让你喜欢的潜在改进。 - ruakh
真的非常感谢。你所概述的模式很有启发性;这是一种很好的模式,可以防止意外的退出代码,同时忽略预期的退出代码...我肯定会使用这种模式来简化我的脚本的主要流程。 - dsummersl
@ruakh 很棒的回答,grep函数真的很有用。谢谢。 - Joe
只有一个小细节需要注意。例如,当你遇到这个:[ -e 文件路径 ]如果文件存在,则返回 true。 - Alberto Perez

23

详细手册中得知:

除了单字符shell命令行选项(参见Set内置命令)之外,还有一些多字符选项可供使用。

然后再看一下set命令的说明:

-e
如果管道(参见Pipelines)失败,则立即退出,管道可以由单个简单命令(参见Simple Commands)、括在圆括号中的子shell命令(参见Command Grouping),或由花括号包含的命令列表中执行的一个命令组成(参见Command Grouping)。返回非零状态。

因此,当您使用bash -e时,如果脚本中的任何命令失败(即返回非零退出状态),整个脚本将立即失败。所以您的grep未匹配到结果,导致返回非零值,而运行bash时指定-e选项,这就关闭了整个脚本。


2
啊,我猜这是来自于 set 命令,但我想在手册中看到它。我一直在想为什么在 man bash 中没有看到它,原来是我直接跳过了列出的选项,错过了其中的一段话,它说:“在 set 内置命令的描述中记录的所有单字符 shell 选项,包括 -o,在调用 shell 时都可以用作选项。” 全部都在那里! - Nagev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接