errexit is not suspended in a pipeline

2023-01-11 Thread Quinn Grier

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

2023-01-11 Thread Chet Ramey

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

2023-01-11 Thread Quinn Grier

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

2023-01-11 Thread Chet Ramey

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

2023-01-11 Thread Quinn Grier

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

2023-01-11 Thread Robert Elz
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