From Sanford, Kurt
Answered By: Jim Dennis
Hi Jim and Dennis,
Actually, "Jim Dennis" is one person, and the original founder of the Answer Gang.... -- Thomas Adam
Regarding the problem with:
echo 1 2 3 | read a b c echo $a $b $c
on Linux ksh (see http://linuxgazette.net/issue57/tag/1.html <http://linuxgazette.net/issue57/tag/1.html> ), why are Red Hat and SuSe using such an old version of ksh? That version of ksh must be quite old because I have been writing UNIX ksh scripts that do "echo 1 2 3 | read a b c" since at least 1996. Why hasn't the Linux version of ksh been updated?
I think you misunderstand my point. Some shells read run the implicit subshell to the right of the pipe (bash, older versions of ksh, etc) while others run it on the left (newer ksh and zsh).
As far as I know POSIX doesn't specify which behavior is correct. Therefore the authors of each shell are left to their own judgement for their own implementations. I personally believe that the new ksh and zsh semantics are better and provide for cleaner shell scripting code.
The test for any Bourne compatible shell to disambiguate one set of semantics from the other:
unset foo; echo bar | read foo; echo $foo
If that prints nothing but a blank line, you're running ash, bash, or and old pdksh or other Bourne shell. If it prints "bar" then you're running Korn '93(?) or newer, or zsh.
Notice that, around any pipe operator in any shell command there is an implicit subshell (child process). The pipe is an inter-PROCESS communications mechanism so there have to be a pair of processes as the writer and reader to and from the pipe. We're simply asking which side of the pipe is handled in the sub process and which is handled in the current process.
Also note that in many cases (when the commands to either side of the pipe are external) the whole issue is moot. Both will be in child processes:
cat ./foo | less
... since cat and less are both external commands both are in subprocesses of the current shell and it doesn't matter what order the forks were in.
The shell will create one subshell (child process), that will create the pipe (using the pipe system call) and dup them to stdin and stdout, then it will create another subshell/process. The (first) child will then close one of the ends of the pipe (either the read or the write) and the (second) grandchild will close the other. Then one of them (with the write end of the pipe still open) will exec*() the cat command while the other (on the read end of the pipe will exec the less command.
There may be other sequences of system calls that net comparable results. You could run strace on a number of shells to see.
|Meet the Gang 1 2 3 4|