[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: wait inside subshell waits for sibling

From: Robert Elz
Subject: Re: wait inside subshell waits for sibling
Date: Tue, 25 Oct 2022 00:58:16 +0700

    Date:        Mon, 24 Oct 2022 16:16:21 +0200
    From:        Emanuele Torre <torreemanuele6@gmail.com>

  | It does not make any sense that you keep insisting that this is about
  | bash internals, or low level system call (especially `wait', `fork',
  | etc),

Since that is the exact opposite of what I have been saying, it
seems that you haven't been bothering to read any of it.

  | and I keep having to repeat that this is only caused by
  |     (CMDS) REDIRS
  | being sematically equivalent, *in bash*, to
  |     (exec REDIRS; CMDS)

I have just spent some time rereading the bash man page, and
I'm afraid I couldn't find that specified anywhere.

Are you sure you're not just inventing this?

Note: I'm not saying that that isn't how bash actually works,
but whether it is anywhere specified to work that way.   bash
has had bugs before, after all.

Further, I don't care if it is implemented that way, provided
that it is done properly.

  | I don't get why you accept that:
  |   'externalcmd' 'a' 'b' > "$(( ++i ))"
  | is not equivalent to:
  |   { 'externalcmd' 'a' 'b' ;} > "$(( ++i ))"

Where did I ever say that?   All I have ever said about
those examples is that what happens to variables altered
as a side effect of an expansion in a redirect is irrelevant
to the question at hand.

  | This behaviour probably originated in ksh (irrelevant note: and was
  | probably implemented because applying redirection after fork() is easier
  | and safer, [...]

Doing redirections in process created to run an external command is
nothing new, it probably came from the original Bourne shell, long
before ksh was even a consideration.

As long as the semantics are maintained as expected, that's fine.
In this case, that will be where the "side effects in redirects
don't work" came from (I expect), as while arithmetic (and lots more)
didn't exist in the original Bourne sh, ${var=default} did.

  | In bash, this same behaviour is also applied, to subshell compound
  | commands. Which makes sense (if you think that the behaviour for simple
  | commands that run external programs makes sense, which I don't
  | necessarily do personally), since they also have a "mandatory" fork()
  | that can be exploited to prevent the work needed to restore file
  | descriptors at the end, and is what inspired the simple command
  | behaviour in the first place.

Once again, it is just fine to do the redirects in the subshell.
The problem here is what is being added to the job table (async process
table) in that subshell, in a form that makes it available for wait,
when it shouldn't be.

        ( cmd & wait )
the only background command in the subshell, and hence the only thing
wait can wait for, is cmd (it doesn't matter what that is).   Nothing
from outside should be possible to wait upon.  Ever.

Any redirect applied to that (as opposed to added inside the subshell)
is not in the subshell, wherever, or however, the shell decides to
implement the redirect, and should not be able to create something that
the wait command inside the shell can ever see.

  | I can agree that I would also like if bash expanded everything in the
  | main shell, and only applied the redirection of the expanded values in
  | the subshell.

You might like that, but that isn't what I said should happen.  It
might be permitted by the standards to do it that way, but it isn't
required.   But being able to wait upon processes in a subshell evironment
not intended to be created there is wrong.

  | I do not agree at all, though, with your idea that variable
  | modifications *should* not be evaluated in the main shell, but
  | everything else should: all the expansions, and/or redirections,
  | regardless of what they are, should be evaluated by the same shell; if
  | they are not, it simply does not make sense.

If you apply "make sense" as the test of how any shell works, you're
in for lots of disappointments.   Much of what we have has been inherited
by what was required to fit the Bourne shell into the limited space
available for a process in a PDP-11.   That resulted in all kinds of
weirdness (including the bizarre quoting rules around ${var-word} type

  | I have used a variable modification in the example simply because it is
  | easier to show.

Sure, but it doesn't help if the rules are different.  They are.

  | How doesn't that make any sense? And what justfies your reasoning since
  | also other shells that apply this change in behaviour (ksh) run
  |     external < <(cmd1) > "$(cmd2)"
  | with `cmd1' and `cmd2' as child processes of the subshell that ends up
  | exec()-ing `external'.

Once again, I don't care what process is a child of what other.   That's
never been what this is about, but you don't seem able to grasp that.

The question is what wait should wait for.   This gets back to the first
point of my first reply.  wait in the shell (the command) is not the same
as wait(2) (the system call).   The latter waits for any child process
that hasn't been waited for.   The wait command looks for completed (or in
bash, stopped) children of the current shell environment, not ones that
exist elsewhere.

Here's another command that illustrates what should happen, and which
bash implements correctly:

        (sleep 100 & exec bash -c wait)

the sleep is executed as a child of the subshell (of whatever shell
is running that command, it doesn't matter what that is).  That
subshell then becomes bash.   Now the sleep is a child process of that
newly created bash (try this with that bash running pstree if you don't
believe it).   The wait command there (correctly) does not wait for
the sleep command to finish - it wasn't created in the bash shell's
execution environment, though it is a child of the bash process.

In your model, how do you explain that?

  | Are you just saying that because you are conviced that this is an issue
  | with the `wait' builtin's handling of process

It is, that is what the original bug report was about.   Fix wait and
the issue goes away.

  | The only reason why
  |     (: & wait) > >(cat)
  | behaves like it does *in bash* is that, *in bash*, it is semantically
  | equivalent to:
  |     (
  |         exec > >(cat)
  |         : &
  |         wait
  |     )

So you keep saying, but I can find zero documentation to support that.

  | and you are surprised because you didn't know at least of that behaviour
  | (and, maybe, also that process substitutions processes count as
  | background jobs).

No, I know that, and if the user had written the command in your
"equivalent to" form, the wait should indeed wait for the process
substitution to finish.   The user could have written it that way,
but did not.

  | Again, that is the exact same for simple commands.
  |     bash-5.1$ { pstree -p -- "$$" ;} > >(cat)

This is a waste of time.   pstree shows the processes.   That's
not what matters, what matters is how the shell builds its table
of known processes (or jobs) and which of them the wait command
will actually wait upon.   Nothing that pstree shows can possibly
reveal that information.

There is no point continuing this discussion, you're clearly of the
opinion that anything that bash happens to do is how it is defined
to work.   I can't imagine given that mindset how you cope when
Chet issues a patch to fix a bug.

I'm done with this nonsense.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]