Re: caller returns wrong line number in command substitution

2022-10-24 Thread Chet Ramey
On 10/18/22 1:47 PM, Andrew Neff via Bug reports for the GNU Bourne Again 
SHell wrote:



Description:
   Using the "caller" command on a line of bash code in a process substitution 
has been incorrect from bash 3.2 through 5.1, but I could write my code in such a way to 
work around it. Some of these workarounds no longer function in bash 5.2. I saw that you 
made some changes to this code [see below], however, I think they introduced another 
regression to how caller calculates the line offset. In the following tests, caller 
should always return the last line in a multi-line bash call, but that is not the case 
now in a process substitution in bash 5.2 (test 5)


Getting line numbers correct for multi-line simple commands has always been
tricky, given the bottom-up way you have to build commands with bison. You
don't really know you're parsing a simple command until you've parsed it,
so you know where you are when you're finished, but not really where you
started.

For most single-line cases, it doesn't matter, but multi-line cases use the
line number when the parser finishes parsing the simple command.

So let's take a look at what happens for this script:

 1  function foobar()
 2  {
 3  caller >&2
 4  }
 5  true <(
 6foobar "test2
 7  ...
 8  bar")

Running this script prints `10'. Why?

Well, it's a combination of things. First, the line number associated with
the `true' command is 8, since that's the line number when bison resolves
the `simple_command' production and we go off and create it.

Now, before that happens, we have to run the process substitution through
the parser so we can find the ending right paren. We use the same code used
to parse command substitutions, since they have the same requirements.
Rather than keep the original text around, which is difficult to do with
how the parser and lexer are structured, bash reconstitutes the text of the
process  substitution from the command it parses. This is syntactically
equivalent, but has the effect of throwing away leading and trailing
sequences of newlines, since those don't contribute to the command. (Your
suspicion about the new command substitution parsing code having an effect
here is correct.)

So when we go to perform word expansion on the process substitution, the
internal line number is 8, since that's the line number of the simple
command, and the command that's run by the shell forked to execute the
process substitution starts with `foobar' (not `\nfoobar').

The shell forks a child, and that child parses the string, starting at
line 8. The command consists of three lines: lines 8, 9, and 10. Since
the current line number is 10 when we finish parsing the simple command
and resolve the production, that's the line number associated with that
command. When the function is called, and `caller' does its thing, the
line number associated with the currently-executing command that called
the function is 10, and that's what it prints.

You should be able to puzzle out what happens for the other cases based
on this description.

--
``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: caller returns wrong line number in command substitution

2022-10-18 Thread Andrew Neff via Bug reports for the GNU Bourne Again SHell
Oops, my mistake. Got some terms mixed up a little there.

Yes, every time I command substitution, I meant process substitution. So that 
release note for "Rewrote the command substitution" most likely nothing to do 
with this.

From: Robert Elz 
Sent: Tuesday, October 18, 2022 7:09 PM
To: Andrew Neff 
Cc: bug-bash@gnu.org 
Subject: Re: caller returns wrong line number in command substitution

There are no command substitutions in any of your examples.
A command substitution is what you get with $( )  (or if you
really like obsolete syntax for some reason, ``).

What you're showing is process substitution.

This has nothing to do with whether or not there is an issue
here that's worth fixing however - but bash's management of
line numbers has always been "close enough" rather than "accurate".

kre



Re: caller returns wrong line number in command substitution

2022-10-18 Thread Robert Elz
There are no command substitutions in any of your examples.
A command substitution is what you get with $( )  (or if you
really like obsolete syntax for some reason, ``).

What you're showing is process substitution.

This has nothing to do with whether or not there is an issue
here that's worth fixing however - but bash's management of
line numbers has always been "close enough" rather than "accurate".

kre




caller returns wrong line number in command substitution

2022-10-18 Thread Andrew Neff via Bug reports for the GNU Bourne Again SHell
Machine: x86_64
OS: linux-musl
Compiler: gcc
Compilation CFLAGS: -g -O2
uname output: Linux cfa1574b05c7 5.10.102.1-microsoft-standard-WSL2 #1 SMP Wed 
Mar 2 00:30:59 UTC 2022 x86_64 GNU/Linux
uname output: Linux 3beae0f31cdf 5.18.18-100.fc35.x86_64 #1 SMP PREEMPT_DYNAMIC 
Wed Aug 17 16:09:22 UTC 2022 x86_64 Linux
Machine Type: x86_64-pc-linux-musl
Docker: docker run -it --rm bash:5.2

Description:
  Using the "caller" command on a line of bash code in a process substitution 
has been incorrect from bash 3.2 through 5.1, but I could write my code in such 
a way to work around it. Some of these workarounds no longer function in bash 
5.2. I saw that you made some changes to this code [see below], however, I 
think they introduced another regression to how caller calculates the line 
offset. In the following tests, caller should always return the last line in a 
multi-line bash call, but that is not the case now in a process substitution in 
bash 5.2 (test 5)

  [quote]
c. Rewrote the command substitution parsing code to call the parser 
recursively and rebuild the command string from the parsed command. This allows 
better syntax checking and catches errors much earlier. Along with this, if 
command substitution parsing completes with here-documents remaining to be 
read, the shell prints a warning message and reads the here-document bodies 
from the current input stream

Repeat-By:
  Here's a small script to repeat the problem:

#!/usr/bin/env bash
function foobar()
{
  caller >&2
}
foobar "test0
...
bar"
true <(foobar "test1
...
bar")
true <(
  foobar "test2
...
bar")
true <(\
  foobar "test3
...
bar")
read -rd '' foo < <(foobar "test4
...
bar") || :
read -rd '' foo < <(
  foobar "test5
...
bar") || :
read -rd '' foo < <(\
  foobar "test6
...
bar")
read -rd '' foo < \
<(foobar "test7
...
bar")
  Results:

  Test Ans 5.1 5.2
  08   8   8
  111  13  13
  215  18  17
  319  21  21
  422  22  22
  526  26  25
  630  29  29
  734  33  33

  Summary:
  Test 0: no command substitution, it works.
  Test 1: the answer is off by the number of lines. So 1 line is right, 2 lines 
if off by one line, 3 lines (as show) is off by 2. If it was 10 lines, the 
answer would be off by 9.
  Test 2: Same offset as Test 1. On bash 3.2-5.1 off by one additional line
  Test 3: Same as test 1
  Test 4: Actually right
  Test 5: Off by -1 lines in bash 5.2, right on bash 3.2-5.1
  Test 6: Always off by -1 lines
  Test 7: Same as Test 6

Fix/Workarounds:
  Only the Test0 and Test4 consistently give the right answer. However for 
readability and other reasons, I don't always want those syntaxes.