Why doesn't bash errexit behave as expected in function calls?

The bash man page says:

Exit immediately if the pipeline (which may consist of one simple command)
a head restraint command enclosed in parentheses, or one of the commands executed as part of a list of commands enclosed in braces ...

So, I suggested that the function should be considered as a list of commands, enclosed in curly braces. However, if you apply a conditional expression to a function call, errexit is no longer stored inside the function body, and it returns the entire list of commands before returning. Even if you explicitly create a subshell inside a function with the errexit function enabled for that subshell, all the commands in the command list are executed. Here is a simple example demonstrating the problem:

function a() { b ; c ; d ; e ; } function ap() { { b ; c ; d ; e ; } ; } function as() { ( set -e ; b ; c ; d ; e ) ; } function b() { false ; } function c() { false ; } function d() { false ; } function e() { false ; } 

 ( set -Eex ; a ) + a + b + false 

 ( set -Eex ; ap ) + ap + b + false 

 ( set -Eex ; as ) + as + set -e + b + false 

Now, if I applied the condition to each of them ...

 ( set -Eex ; a || false ) + a + b + false + c + false + d + false + e + false + false 

 ( set -Eex ; ap || false ) + ap + b + false + c + false + d + false + e + false + false 

 ( set -Eex ; as ) + as + set -e + b + false + c + false + d + false + e + false + false 
+7
bash
source share
3 answers

You started quoting manual , but then you reduced the bit explaining this behavior, which was in the very following sentence:

-e Exit immediately if the pipeline, which may consist of one simple command, a subshell command enclosed in parentheses, or one of the commands executed as part of the list of commands enclosed in curly braces, returns a non-zero status. The shell does not exit if the failed command is part of the command list immediately after the while or until keyword, part of the test in the if , part of any command executed in the && or || except for the command following the last && or || , any command in the pipeline, but the last one, or if the return status of the commands is flipped with ! .

+8
source share

The bug- bash mailing list contains an explanation of Eric Blake in more detail about the features:

Short answer: historical compatibility.

...

Indeed, the correct behavior specified by POSIX (namely, that 'set -e' is completely ignored throughout the body of f (), since f was invoked in a context that ignores "set -e") is not intuitive. But he is standardized, so we must live with him.

And some words about whether set -e can be used to achieve the desired behavior:

Because, as soon as you are in a context that ignores set -e, the historical behavior is that there is no other way to get it back, since all code is in an ignored context. As it was done 30 years ago, before the shell functions were really considered, and we were stuck with this bad design decision.

+3
source share

Not an answer to the original question, but a workaround for the main problem: configure the error trap :

 function on_error() { echo "error happened!" } trap on_error ERR echo "OK so far" false echo "this line should not execute" 

The reason for the behavior itself is correctly explained in other answers (basically this is the expected behavior of bash according to the manual and POSIX): stack overflow

0
source share

All Articles