Date: Mon, 10 Apr 2023 10:30:08 -0400
From: Chet Ramey <[email protected]>
Message-ID: <[email protected]>
| The different semantics are that the standard specifies the status of the
| simple command in terms of the command substitution that's part of the
| assignment statement, so you have to hang onto it for a while.
I suspect that's because you are treating the assignments (more or less)
as statements of their own, and expanding and then assigning each, one by one,
left to right as you encounter them.
If you treated several var assigns just like they were args to commands,
expanded them all (for this purpose, left to right) and then run the
command - which involves putting the values to be assigned from var-assigns
into the environment of the command to be run ... in this case, the null
command, so that means the assignments affect the current shell environment,
then there is no issue, and no real need to "hang onto it for a while".
In the case where there's no command, the exit status of the last cmdsub
is simply there, for the next command to use (not this one, because there
are no more expansions to be made) - in the case where there is a command
the command execution comes next, and the exit status from that overrides
the exit status from the command substitution, before there is any possibility
of the cmdsub status (for the one that might matter, or any earlier ones
that might also have been executed, which are already lost) become visible,
to anything, as no more expansions are happening at this point.
But because the standard doesn't actually say which order these things need
to be evaluated, but does say how $? is supposed to be affected, the
implementations can get messy to handle all of this properly, if the
implementation chooses a different way of handling the unspecified part
(which really, is unspecified just because some early implementations did that).
Note, that before we do any of this (var-assign, and redirect, processing)
we have already expanded all the rest of the command line, the words that are
not related to redirects or var-assigns, we have the command name (if any)
and know if it is there at all (or not, a null command) and if it is a
built-in of some kind (so whether or not we shall fork() ... create a new
shell environment) or not - and if we want, when there is to be one, most
of the rest (redirects and var-assigns) can be expanded in that new
environment (in the child process). Or not. That's all just implementation
detail (provided we don't leave any inappropriate results in the parent
shell environment .. which means special care if the fork() is implemented
using vfork()).
kre