Subject: Re: make -j and failure modes
To: Ben Harris <bjh21@NetBSD.org>
From: Robert Elz <kre@munnari.OZ.AU>
List: tech-userlevel
Date: 12/10/2003 22:22:09
    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