[comp.unix.questions] shell &&, || constructs

chris@mimsy.UUCP (Chris Torek) (10/02/88)

In article <826@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>command1 || command2               # do command2 only if command1 failed
>command1 && command2               # do command2 only if command1 succeeded
>while command1; do commands; done  # while command1 succeeds do the commands
>until command1; do commands; done  # until command1 succeeds do the commands
>if command1; do commands; done     # if command1 succeeds do the commands
>
>And now we're at it:
>
>set -e    # exit - if the shell's not interactive - as soon as a command fails

With the exception of `set -e', these constructs exist in csh as well,
although the syntax differs:

	command1 || command2
	command1 && command2
	while ({ command1 })
		commands
	end
	while (! { command1 })
		commands
	end
	if ({ command1 }) then
		commands
	endif

The parentheses around { command1 } may sometimes be elided.  But
beware!: the effect of && and || were reversed in the 3BSD C shell,
and are still reversed in some derivative C shells.  (It is fixed
in 4.3BSD; I am not sure how far back the rot goes.)

Regarding `set -e': again, beware!  Old versions of /bin/sh will
exit if the test portion of an `if', `while', or `until' returns
a nonzero status, if `-e' is set.  (These are fixed in 4.3BSD-tahoe.)
In addition, `command1 || command2' will exit if command1 fails.
(It will also exit if command2 fails, but that seems sensible.
I believe SysV's /bin/sh does *not* exit if command2 fails.  If
this is considered correct behaviour, let me know, because I just
`fixed' this to work the way I think seems sensible.)

Incidentally, this bug in the 4BSD `/bin/sh'es accounts for
makefile lines like

	-[ -d ${DESTDIR}${LIBDIR} ] || mkdir ${DESTDIR}${LIBDIR}

where the `-' before the test `[' seems erroneous: it tells make to run
sh without the -e flag, so that the mkdir can happen.  An alternate
workaround is to use

	set +e; [ -d ${DESTDIR}${LIBDIR} ] || mkdir ${DESTDIR}${LIBDIR}

which has the advantage that if mkdir fails, make stops.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.ARPA (Doug Gwyn ) (10/03/88)

In article <13810@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
-Regarding `set -e': again, beware!  Old versions of /bin/sh will
-exit if the test portion of an `if', `while', or `until' returns
-a nonzero status, if `-e' is set.  (These are fixed in 4.3BSD-tahoe.)
-In addition, `command1 || command2' will exit if command1 fails.
-(It will also exit if command2 fails, but that seems sensible.
-I believe SysV's /bin/sh does *not* exit if command2 fails.  If
-this is considered correct behaviour, let me know, because I just
-`fixed' this to work the way I think seems sensible.)

The correct behavior is for "failure || failure" to count as an error
with respect to "set -e", and the other combinations of operands to ||
to not count as an error.  BRL's SVR2-based shell works this way; I
seem to recall that there was a bug in the eflag handling that had to
be fixed, although it might not have involved || (I don't remember).

Obviously you don't want to make your shell scripts depend on this if
you're concerned about portability.

prl@iis.UUCP (Peter Lamb) (10/04/88)

In article <13810@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>Regarding `set -e': again, beware!  Old versions of /bin/sh will
>exit if the test portion of an `if', `while', or `until' returns
>a nonzero status, if `-e' is set.  (These are fixed in 4.3BSD-tahoe.)
>In addition, `command1 || command2' will exit if command1 fails.
>(It will also exit if command2 fails, but that seems sensible.
>I believe SysV's /bin/sh does *not* exit if command2 fails.  If
>this is considered correct behaviour, let me know, because I just
>`fixed' this to work the way I think seems sensible.)
>
>In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
>Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

And some not-so-old /bin/sh's. This is one of my pet peeves with
Ultrix. Up to and including Ultrix 2.2 DEC's /bin/sh does just this,
which makes makefiles using conditionals difficult to do right.
DEC's /bin/sh5 (SysV shell) works correctly in this respect, but...
because DEC added the { command ; command } syntax to their shell,
and then put this in just about every critical shell script on the system,
you can't make the SysV shell /bin/sh (or rather you can, but you
won't be able to boot multi-user nor load DEC layered products :-) .

And just to support Chris' case that sh -e should not exit if the
command in a conditional returns non-zero status;

if you have a conditional, it is usually because you *EXPECT* the
thing to fail sometimes, otherwise you wouldn't have bothered!


-- 
Peter Lamb
uucp:  seismo!mcvax!ethz!prl	eunet: prl@ethz.uucp	Tel:   +411 256 5241
Institute for Integrated Systems
ETH-Zentrum, 8092 Zurich

leo@philmds.UUCP (Leo de Wit) (10/05/88)

In article <639@eiger.iis.UUCP> prl@iis.UUCP (Peter Lamb) writes:
  [ ]...
|And just to support Chris' case that sh -e should not exit if the
|command in a conditional returns non-zero status;
|
|if you have a conditional, it is usually because you *EXPECT* the
|thing to fail sometimes, otherwise you wouldn't have bothered!

Of course, but you must take two other things into account:

1) the conditional is not the exit status of a (1) command, but of a
command list. I can even write something down like:

if 
<shell script here>
then
<other stuff here>
fi

and put a whole shell script into the conditional clause (not that this
is common practice). Now do you want -e to be turned off for the whole
clause (see also beneath) ??

2) -e is used when successfull execution of the commands in a script is
a must; take for instance the 'Make' example. Why would the commands in
the commandlist of a conditional clause suddenly be all that different?
If they are, they should be executed by a different shell (my humble
opinion).

A minimal change to the meaning of -e could add the functionality
required without hardly breaking existing code (i.e. the code that
already depends on shells that turn off -e for conditionals):

The last command of the conditional clause's command list should not
cause an exit if it fails and -e is set.

a) Since most clauses are just a simple command (e.g. test) this works
OK in these cases.

b) The change allows termination of the script on all other commands in
the command list, thus respecting the meaning of -e.

c) If you don't want the b)-haviour, there are some alternatives:
you can execute the commands in a subshell; note that this has to
provide for a means of turning off -e which the BSD /bin/sh doesn't
seem to have, since -e is inherited.
Invoking this shell by sh -c seems to avoid this problem:

if sh -c "<non-critical commands here>"; then etc; fi

Just some oil to keep the fire bourning... 8-)

                                             Leo.

prl@iis.UUCP (Peter Lamb) (10/07/88)

In article <831@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>In article <639@eiger.iis.UUCP> prl@iis.UUCP (Peter Lamb) writes:
>|if you have a conditional, it is usually because you *EXPECT* the
>|thing to fail sometimes, otherwise you wouldn't have bothered!
>
>Of course, but you must take two other things into account:
>1) the conditional is not the exit status of a (1) command, but of a
>command list. I can even write something down like:
>if 
><shell script here>
>then
><other stuff here>
>fi
>and put a whole shell script into the conditional clause (not that this
>is common practice). Now do you want -e to be turned off for the whole
>clause (see also beneath) ??

Yes.

>A minimal change to the meaning of -e could add the functionality
>required without hardly breaking existing code (i.e. the code that
>already depends on shells that turn off -e for conditionals):
>The last command of the conditional clause's command list should not
>cause an exit if it fails and -e is set.

This seems unnecessarily complicated. I would settle for *either*
the BSD /bin/sh behaviour (always exit on -e) or for the SysV
behaviour (don't exit if the return status of the list is `tested')
provided

	a) It was always the same and
	b) It was clearly documented.


What really bugs me about Ultrix is not so much the -e behavior as their
totally crazy hacking of /etc/rc* and /etc/setld and other critical
system scripts to put in their cutesy

[ -f totally_nonstandard ] && { bizarre ; whacky }

replacement of the standard `if' tests, so that I *can't* use the SysV
shell (also provided by DEC in Ultrix as /bin/sh5) as /bin/sh
because I won't be able to boot my machine multiuser any more!
-- 
Peter Lamb
uucp:  seismo!mcvax!ethz!prl	eunet: prl@ethz.uucp	Tel:   +411 256 5241
Institute for Integrated Systems
ETH-Zentrum, 8092 Zurich