Historically, the Bourne shell and the Korn shell behaved differently. Since the two sides of a pipe are executed in parallel, in different processes, it isn't possible for variable assignments on both sides to be preserved in the rest of the script. Given
unset a; a=b | a=c, the variable
a can end up unset, or equal to
b, or equal to
c; it can't be equal to both
In the Bourne shell, each side of the pipe operator runs in a subshell (i.e. in a separate shell process), so variable assignments are not preserved. In the Korn shell, the right-hand side of a pipe is executed in the original shell process.
As in many cases where existing behaviors differed, POSIX allows both behaviors. As stated in the section “Shell Execution Environment”:
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 will be executed in the current shell environment.
Some systems have implemented the last stage of a pipeline in the current environment so that commands such as:
command | read foo
foo in the current environment. This extension is allowed, but not required; therefore, a shell programmer should consider a pipeline to be in a subshell environment, but not depend on it.
In practice, commands other than the last always run in a subshell, except for some optimizations where the behavior is the same unless you look closely at the number of processes that are created. That is, no shell has
unset a; a=b | true; echo $a print
Most other Bourne-like shells, such as ash, dash, bash, pdksh and mksh, behave like the Bourne shell. Zsh behaves like the Korn shell. Recent versions of bash can switch to the ksh behavior with
shopt -s lastpipe.