tech-userlevel archive

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

/bin/sh redirect without command failure mode

A question on what the shell should do:

POSIX specifies that a redirect error on a special builtin command
causes the shell to exit.

It also specifies that a redirect error on a utility that isn't a special
builting doesn't cause the shell to exit (but just fail the utility with
a non-zero exit status (the utility does not run)).

[Aside: "exit" here for an interactive shell means abort the current
command - all of it - and return to  issue the PS1 prompt and read
the next command.   In all cases there is a diagnostic to stderr.]

However POSIX does not specify which behaviour is appropriate
for a redirect when there is no utility (special builtin, or otherwise)
at all.

The NetBSD shell's behaviour is to exit.   All other shells seem to
not exit (treat it as a command error, and just set the exit status).

Some history:   As I remember it, the original Bourne shell always
exited on a redirect error (with exit as defined above).   For most
utilities (commands) the redirect happened (and still happens) after
the shell has forked, ready to exec the command - so the effect was
that the child shell exited with a non-zero status (which is still
what happens after the shell has forked).

When the shell did not fork, a redirect error would cause the shell
to exit (so "cd / >/no/such/file" would have caused the exit).
Somwehere along the way the behaviour changed, so that normal
builtin commands (like cd, umask, ...) don't exit on a redirect error.
That is what POSIX now specifies (and what we do.)

But no-one really considered what happens when there is no
utility ("command" but that word has a broader meaning in the
shell, and includes things like "X=1" where there is no utility,
just the variable assignment)..

For the case of no utility, but var-assigns exist, we want the
thing to act like a special builtin (and that is what the NetBSD
shell does) because that way the results of the variable assignment
persist in the shell (a var-assign before another utility simply sets the
environment for the utility, and does not persist in the shell ... again
this originally related to fork/no-fork - when the shell forked, the
var assigns happened there, and  the parent did not see them.)

Clearly that behaviour is required, we do not want to have to write

	X=1 :

in order to get X to be 1 on the next line.    (All shells do it this way.)
But if we just do


(or even
	X=1 </no/such/file

do we treat that as a special builtin, or not?   If it is, the shell will exit
(again, as defined above) when the redirect fails,    If not, then it won't.

The FreeBSD shell "solves" this by treating the case

	X=1 </no/such/file

as an ordinary builtin, while it does the redirect, and then switches it to
being a special builtin for the assignment.   That gives them the same
behaviour as bash/dash/mksh/yash/zsh/... (including /bin/ksh on NetBSD).

So, what would you prefer i do?  Copying the FreeBSD method (though the
implementation details differ) is easy (I have an uncommitted version that
acts this way), but seems a bit of a klyudge to me.   On the other hand, being
the only shell to be different isn't a good state either, and were POSIX ever
to actually specify something here, I have little doubt that it would be to do
what all the other shells do.

One final note. redirects without command words are a very rare beast.
They do nothing normally useful, if the redirect is to persist, the "exec" utility 
(which is a special builtin, and so a redirect error causes the shell to exit)
needs to be used it is unlikely that anything much would be affected
by a change.  However as we have it now, it is harder to test if a file
descriptor is open or not, the "command" utility is needed (with no other
command word, or with a special builtin, it removes the "special" part,
so we can now do
	command <&3
which behaves just the same as
in other shells (the "command" version should work everywhere)
which can be used to test whether fd 3 is open or not (arrangements
ened to be made to direct stderror to /dev/null if the message isn't wanted).


Home | Main Index | Thread Index | Old Index