Date: Wed, 10 Dec 2003 14:48:28 +0000 (GMT)
From: Ben Harris <bjh21@NetBSD.org>
Message-ID: <Pine.SOL.4.58.0312101410440.12032@draco.cus.cam.ac.uk>
| On Wed, 10 Dec 2003, James Chacon wrote:
|
| > The subshell should exit by normal rules and based on the fact that -e
| > transfers to it. (and return according to the rules for subshells
| > which is the exit code from the last command).
| >
| > Past that I think it's obvious then that the parent would exit as it's got
| > the e flag set and it just had a command exit non-zero and it's not part
| > of the exclusion list of ones to ignore for checking on error.
|
| So you think that that the parent should treat the entire group of
| commands in the subshell as a single command, and exit if it fails, yes?
I think that's what he is saying, but I believe it is clearly wrong.
It is absurd to require the shell to look inside the commands run by the
sub-shell and see if they're the kind that should cause "set -e" mode to
cause the shell to exit - but not doing so would mean the parent shell exiting
if the sub-shell exits with an error code, even though that error code didn't
actually cause the sub-shell to exit 1 because of the -e.
That is, a couple of examples...
delta$ sh -ec 'false && echo foo'
no output, of course, false is false...
delta$ sh -ec 'false || echo foo'
foo
the "false" does not cause the shell to exit, because it is in a ||
sh -ec 'false && echo foo
echo ok'
ok
Same here, no "foo", no exit from the shell either.
delta$ sh -ec 'false || false'
delta$ echo $?
1
Shell exit's 1 from this, but that's because false does exit 1, not
because of -e
as shown by ...
delta$ sh -ec 'false || exit 2'
delta$ echo $?
2
delta$ sh -ec '(false || false) ; echo ok'
ok
The sub-shell exit's 1 in this case, just as it does in the
following
delta$ sh -ec '(false) ; echo ok'
ok
In the latter case, the 'exit 1' from the sub-shell was because of the
command exiting with an error, as seen by ...
delta$ sh -ec '(false; echo bad) ; echo ok'
ok
the 2nd command in the sequence was aborted (as it should be).
Now for the example that shows how absurd the expectation is ...
delta$ sh -ec 'false && echo bad ; echo ok'
ok
That one is clearly OK, as set -e doesn't apply to commands in &&
(without doubt, or && and || would be useless)
But
delta$ sh -ec '(false && echo bad) ; echo ok'
ok
what was being requested was that this one should not do the echo ("ok")
because of the sub-shell doing the exit 1 (because of executing the
false command, not because of set -e).
That's too dumb to contemplate, and attempting to write either the code,
or the specification, to make anything different happen is way too complex
to even consider.
| This seems entirely sane to me, and seems to match the behaviour of shells
| other than Bash,
Then I think they're broken, bash, and the netbsd shell, seem to be doing
exactly the right thing.
| but I'm having difficulty reconciling it with the POSIX
| definition of "-e", and in particular its statement that "-e" only applies
| to simple commands, which the group is not.
Clearly not, and quite correctly, and I'm not surprised at your difficulty.
| I think this is probably a
| bug in POSIX, but I'm not sure I have the energy to pursue it right now.
It would be a wasted effort anyway, I sincerely hope, so please don't.
| I think that adding a host tool to work around our inability to write
| portable shell scripts is silly.
Agreed.
| If we really can't trust ourselves,
| nbmake could always add "|| exit $?" to every command it passes to the
| shell, hence avoiding the need to trust "sh -e" at all.
That or something.
The underlying problem here, in the original example, wasn't the sub-shell
in any case, it was the "&&" - which clearly causes the -e to be ignored
(whether the command that fails is the first, or second, in the && sequence).
That much is beyond doubt.
The suggestion to replace the '&&' with ';' is fairly interesting, relying
upon the -e to abort the 2nd command if the first fails - except that relies
upon the commands all being simple ones (probably OK for just "cd somewhere")
and also assumes that no-one every uses "make -i" to defeat the -e - in that
situation the && really is needed to get the same effect.
kre