errexit is not suspended in a pipeline
In the documentation for set -e, the Bash manual says that errexit is suspended in all but the last command of a pipeline: The shell does not exit if the command that fails is part of [...] any command in a pipeline but the last The documentation also of course says that errexit suspension cascades into compound commands: If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. This means the following script should output both "1" and "2": (false; echo 1) | cat set -e (false; echo 2) | cat However, it only outputs "1". I tested in Bash 4.0 through 5.2. Is this a bug in the behavior, or is it a bug in the documentation?
Re: errexit is not suspended in a pipeline
On 1/10/23 9:36 PM, Quinn Grier wrote: In the documentation for set -e, the Bash manual says that errexit is suspended in all but the last command of a pipeline: The shell does not exit if the command that fails is part of [...] any command in a pipeline but the last That's not what that says. It doesn't say that the command doesn't fail, or that `set -e' isn't active; it says how it affects the exit status of the pipeline. This means that a non-terminal pipeline element failing doesn't determine whether or not the shell exits, since it doesn't affect the pipeline's exit status. I'm not sure why you included the `part of' in your quote, since it clearly applies to the text following it: "part of the command list immediately following as while or until keyword, part of the test following ..." -- ``The lyf so short, the craft so long to lerne.'' - Chaucer ``Ars longa, vita brevis'' - Hippocrates Chet Ramey, UTech, CWRUc...@case.eduhttp://tiswww.cwru.edu/~chet/
Re: errexit is not suspended in a pipeline
On 2023-01-11 06:44, Chet Ramey wrote: On 1/10/23 9:36 PM, Quinn Grier wrote: In the documentation for set -e, the Bash manual says that errexit is suspended in all but the last command of a pipeline: The shell does not exit if the command that fails is part of [...] any command in a pipeline but the last I'm not sure why you included the `part of' in your quote, since it clearly applies to the text following it: "part of the command list immediately following as while or until keyword, part of the test following ..." I agree, my quotation of that documentation is wrong, as "part of" does not apply to "any command in a pipeline". However, I still think my example script shows something fishy. Here's another example script that might help show what I mean. This one goes through all of the apparent contexts where set -e is ignored: # Without set -e, all "echo" commands run. while (false; echo -n 1); do break; done # Outputs 1 until (false; echo -n 2); do break; done # Outputs 2 if (false; echo -n 3); then :; fi# Outputs 3 (false; echo -n 4) && : # Outputs 4 (false; echo -n 5) || : # Outputs 5 (false; echo -n 6) | cat # Outputs 6 ! (false; echo -n 7) # Outputs 7 echo # With set -e, all "echo" commands should still run despite # the fact that "false" would normally cause an exit before # "echo", as these are all contexts where set -e is ignored. set -e while (false; echo -n 1); do break; done # Outputs 1 until (false; echo -n 2); do break; done # Outputs 2 if (false; echo -n 3); then :; fi# Outputs 3 (false; echo -n 4) && : # Outputs 4 (false; echo -n 5) || : # Outputs 5 (false; echo -n 6) | cat # No output (!) ! (false; echo -n 7) # Outputs 7 echo The output of this script is: 1234567 123457 Why is "6" missing from the second line?
Re: errexit is not suspended in a pipeline
On 1/11/23 4:04 PM, Quinn Grier wrote: On 2023-01-11 06:44, Chet Ramey wrote: On 1/10/23 9:36 PM, Quinn Grier wrote: In the documentation for set -e, the Bash manual says that errexit is suspended in all but the last command of a pipeline: The shell does not exit if the command that fails is part of [...] any command in a pipeline but the last I'm not sure why you included the `part of' in your quote, since it clearly applies to the text following it: "part of the command list immediately following as while or until keyword, part of the test following ..." I agree, my quotation of that documentation is wrong, as "part of" does not apply to "any command in a pipeline". However, I still think my example script shows something fishy. Here's another example script that might help show what I mean. This one goes through all of the apparent contexts where set -e is ignored: They are not all contexts where set -e is ignored. There is a distinction between the effect of -e being ignored and the shell not exiting when a command fails. If you don't like the bash manual page wording, here's what POSIX says: "1.The failure of any individual command in a multi-command pipeline shall not cause the shell to exit. Only the failure of the pipeline itself shall be considered. 2.The -e setting shall be ignored when executing the compound list following the while, until, if, or elif reserved word, a pipeline beginning with the ! reserved word, or any command of an AND-OR list other than the last. 3.If the exit status of a compound command other than a subshell command was the result of a failure while -e was being ignored, then -e shall not apply to this command. This requirement applies to the shell environment and each subshell environment separately. For example, in: set -e; (false; echo one) | cat; echo two the false command causes the subshell to exit without executing echo one; however, echo two is executed because the exit status of the pipeline (false; echo one) | cat is zero." -- ``The lyf so short, the craft so long to lerne.'' - Chaucer ``Ars longa, vita brevis'' - Hippocrates Chet Ramey, UTech, CWRUc...@case.eduhttp://tiswww.cwru.edu/~chet/
Re: errexit is not suspended in a pipeline
On 2023-01-11 13:20, Chet Ramey wrote: They are not all contexts where set -e is ignored. There is a distinction between the effect of -e being ignored and the shell not exiting when a command fails. If you don't like the bash manual page wording, here's what POSIX says: "1.The failure of any individual command in a multi-command pipeline shall not cause the shell to exit. Only the failure of the pipeline itself shall be considered. 2.The -e setting shall be ignored when executing the compound list following the while, until, if, or elif reserved word, a pipeline beginning with the ! reserved word, or any command of an AND-OR list other than the last. 3.If the exit status of a compound command other than a subshell command was the result of a failure while -e was being ignored, then -e shall not apply to this command. I see. I had mistakenly thought that Bash's set -e description was definitively listing the contexts in which set -e is ignored, not just contexts in which set -e may not have the intended effect. Really it's POSIX that definitively lists the contexts in which set -e is ignored, as you explain above. The lists are almost the same, with the only difference being that Bash also lists "any command in a pipeline but the last". This is a context in which set -e is *not* ignored but may also not have the intended effect, as the pipeline ignores the exit statuses of all non-last commands (except if set -o pipefail is being used). Thank you for clearing this up for me.
Re: errexit is not suspended in a pipeline
What should be the real issue here is that the -e option is mostly a VERY poor idea to ever use, unless you really know how it works, and only people who are either shell implementors (and perhaps not even all of them) and very long time shell users who have been bitten by attempts to use -e, tend to be in that category. Almost universally, the advice is "don't use -e".People keep wanting to use it as some kind of shortcut to solve all their error handling problems -- easy, just have the shell error out and stop whenever something fails. Sounds great. But then all the times when it either works "too well", or doesn't work when it was expected to, start to bite, and you end up spending far more time fighting to try and make -e do what you wanted it to do, than if you simply looked at every command, and for any where failure should result in the script aborting, just add || fail "xyz produced status $?" to those commands, and never use -e. That works exactly as coded, where coded, and with messages that (if we make them) make sense to the user. We, of course, also add (right up near the beginning): fail() { printf >&2 '%s\n' "$*"; exit 1; } unless that, or something similar (in which case just use it) already exists. Use whatever exit status makes sense - even make it an arg to "fail" if you like, though you don't get any option if you have sh abort because of -e. kre