Newlines in ERR trap affect caller 0 line number

2021-10-31 Thread Quinn Grier

Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-musl
Compiler: gcc
Compilation CFLAGS: -g -O2 -Wno-parentheses -Wno-format-security
uname output: Linux e4cb51356521 5.11.0-38-generic #42~20.04.1-Ubuntu SMP Tue 
Sep 28 20:41:07 UTC 2021 x86_64 Linux
Machine Type: x86_64-pc-linux-musl

Bash Version: 5.1
Patch Level: 8
Release Status: release

Description:
When an ERR trap includes newlines, the line number returned by
"caller 0" is affected.

Repeat-By:
Here are some examples.

$ cat -n 1.bash
 1  set -E -e
 2  f() { i=0; while caller $((i++)); do :; done; }
 3  trap 'f' ERR
 4  false
$ bash 1.bash
4 main 1.bash

$ cat -n 2.bash
 1  set -E -e
 2  f() { i=0; while caller $((i++)); do :; done; }
 3  trap '
 4  f' ERR
 5  false
$ bash 2.bash
6 main 2.bash

$ cat -n 3.bash
 1  set -E -e
 2  f() { i=0; while caller $((i++)); do :; done; }
 3  trap '
 4  
 5  f' ERR
 6  false
$ bash 3.bash
8 main 3.bash

$ cat -n 4.bash
 1  set -E -e
 2  f() { i=0; while caller $((i++)); do :; done; }
 3  trap 'f' ERR
 4  g() { false; }
 5  g
$ bash 4.bash
4 g 4.bash
5 main 4.bash

$ cat -n 5.bash
 1  set -E -e
 2  f() { i=0; while caller $((i++)); do :; done; }
 3  trap '
 4  f' ERR
 5  g() { false; }
 6  g
$ bash 5.bash
6 g 5.bash
6 main 5.bash

$ cat -n 6.bash
 1  set -E -e
 2  f() { i=0; while caller $((i++)); do :; done; }
 3  trap '
 4  
 5  f' ERR
 6  g() { false; }
 7  g
$ bash 6.bash
8 g 6.bash
7 main 6.bash



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 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 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.