Shell – What’s the POSIX specification on behavior of built-in commands with redirections and/or piping

io-redirectionpipeposixshellshell-builtin

Consider the following commands:

exit > /dev/null
exit | cat

On a few shells (ksh, bash, (d)ash), the behavior is the same: The first command causes the shell to quit immediately, while the second one has no visible behavior.

I concluded that the first command did not involve a fork(2), while the second command involved two (one to execute exit, the other execve(2)s to cat).

I looked at the POSIX specification section 2.14, but didn't find anything explicitly stating this.

Is it specified by POSIX that the some commands must not fork(2) while some others must? Is spawning a subshell for the first command acceptable by the standard?


I am aware that ( exit ) should never exit the current shell, because the parentheses spawns a sub-shell that actually executes the exit command, which means the sub-shell will exit immediately. However, I am unsure about redirections and piping, since there's no parenthesis here.

I am asking this question because in a recent course lab, we're told to implement a minimal Unix shell. There are many "optional features" that we can implement, for additional points. One of those "optional features" is combined redirection and pipelining. We had a few different implementations and I believe some are closer to the specified behavior of POSIX than others, and therefore I'd like to know how the actual behavior is specified.

Best Answer

Well (exit),

The exit utility shall cause the shell to exit from its current execution environment [...]

and (2.12. Shell Execution Environment)

A subshell environment shall be created as a duplicate of the shell environment, [...] Additionally, each command of a multi-command pipeline is in a subshell environment; as an extension, however, any or all commands in a pipeline may be executed in the current environment. All other commands shall be executed in the current shell environment.

So the exit in a pipeline runs in an execution environment/subshell of its own, and exits only that, while the one in the simple command exit > /dev/null runs in the main shell environment. (As noted in the comments, the redirection doesn't really affect it at all.)

Note that the part in the middle of the second quote means that some shell might run all the commands of a pipeline in the main environment, thus exiting the whole shell even in that case. In practice, that's more commonly done for the last command of a pipeline.

In Bash with lastpipe, for example:

$ bash -c 'true | exit; echo end.'
end.

But

$ bash -O lastpipe -c 'true | exit; echo end.'

doesn't print anything.