Bash shell 命令行的参数 -e 的含义是什么?

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

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

81701 次浏览

The -e option means "if any pipeline ever ends with a non-zero ('error') exit status, terminate the script immediately". Since grep returns an exit status of 1 when it doesn't find any match, it can cause -e to terminate the script even when there wasn't a real "error".

If you want to keep the -e option, but also have a grep command that might validly find no matches, you can append || : to the grep command. This means "or, if the grep command returns a non-zero exit status, run : (which does nothing)"; so the net effect is to disable -e for the grep command. So:

grep PATTERN FILE... || :

Edited to add: The above approach discards every error: if grep returns 1 because it found no matches, that's ignored, but also if grep returns 2 because there was an error, that's ignored, and if grep isn't in the path (so Bash returns 127), that's ignored — and so on. So, rather than :, it's probably better to use a command that checks the result code and re-issues the error if it's something other than 1. For example:

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

But this destroys the exit status; usually, when a failed command terminates a Bash script with -e, the script will return the command's exit-status, but in the above example, the script will just return 1. If (and only if) we care about that, we can fix it by write something like this:

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

(first line c/o dsummersl's comment).

At this point, it's probably best to create a shell function to handle this for us:

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

(note the use of return rather than exit; we'll let -e handle the exiting when appropriate); this way, we can just write:

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

In fact, since we most likely want to use this function for all occurrences of grep in this script, we can actually just name the function 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

(note the use of command to bypass the shell function within its own body: we want the function to call the regular program grep, rather than to recurse infinitely).

From the fine manual:

In addition to the single-character shell command-line options (see The Set Builtin), there are several multi-character options that you can use.

And then if we look at what set has to say:

-e
Exit immediately if a pipeline (see Pipelines), which may consist of a single simple command (see Simple Commands), a subshell command enclosed in parentheses (see Command Grouping), or one of the commands executed as part of a command list enclosed by braces (see Command Grouping) returns a non-zero status.

So when you say bash -e, if any command in the script fails (i.e. returns a non-zero exit status), then the whole script immediately fails. So your grep is returning a non-zero value because it isn't matching and that's shutting down the whole script if you specify -e when running bash.