Date:        Fri, 12 Dec 2025 08:57:11 -0500
    From:        Chet Ramey <[email protected]>
    Message-ID:  <[email protected]>

  | >> $ f=~/f; echo color: red > $f; foo=$( awk '/^color: / {print $2}' ~/f 
);set -x;cat

  | I'm leaning towards this explanation, since this only happens with gawk,
  | and not, for instance, /bin/echo. If bash were corrupting file descriptors,
  | it would be doing it for every external command.

The latter I'd agree with, while not necessarily absolving bash of all
blame - I'd like to know what happens on a fedora (or any other linux
where the same occurs) when using dash, yash, or one of the ksh variants (or
even zsh).

I'm guessing that the difference between using awk, and most other
processes that might be used (echo, sed, even cat probably) inside the
cmdsub, is that awk (gawk) might be doing

        freopen(filename, "r", stdin);

or something similar, which it is perfectly entitled to do (ie: doing that
is certainly not, of itself, a bug).   Even assuming that in the subshell
environment, bash implements the above as if it were "exec awk" (which it
probably does), that is still in a subshell environment, and should not be
able to affect the parent shell environment, nor any command it starts later
(such as the final "cat" above), not counting what the cmdsub writes to its
stdout of course.

If bash happened to be using the linux clone() sys call, instead of fork()
for that, attempting to try and gain some of the benefits that vfork() would
usually offer (not copying the address space, etc) as vfork() as defined can't
really be used for cmdsubs,  Those write to the parent shell, usually through
a pipe, if no exec has been done (ie: probably not the case above) the parent
is hanging in the vfork() until one does happen (and not in a later fork()'d
process) or until the subshell exits - so is unable to read from its end of
the pipe.  If the volume of output from the cmdsub is bigger than a pipe
buffer, the subshell will hang, the parent is already hung - and you can see
what happens after that (or more accurately, not see, as nothing happens to 
see).    So, perhaps using clone() might make for a cheaper fork() - but
whatever is being shared must not be allowed (for semantic correctness) to
affect the parent shell.   If the file descriptor table were one of the things
being shared, and the subshell were to do a freopen() as above, then the
parent's stdin would be altered.

NB: all that is 100% speculation, I have no idea how bash actually implements
any of that (don't bother telling me I can just read the source, there are
reasons that make that impossible).

If something like that isn't happening, ie: bash is using fork(), then
whatever bugs gawk or GNU cat might have, there must also be a kernel bug
for the symptoms described to occur.

That's what knowing what happens if dash were to execute the same command
would reveal - if the same problem occurs, then I'd say there's a kernel bug,
and simply ignore the issue here (that is, wrt bash).   If it doesn't happen
with dash (or yash, etc) then in some way, it looks as if bash must be
contributing to what is going on - though whether that amounts to a bug or
not would depend upon just what it is doing which, at least, is the catalyst
for the problem.

kre



Reply via email to