The right explanation has already been given by jsbillings and geekosaur, but let me expand on that a bit.
In most shells, including bash, each side of a pipeline runs in a subshell, so any change in the shell's internal state (such as setting variables) remains confined to that segment of a pipeline. The only information you can get from a subshell is what it outputs (to standard output and other file descriptors) and its exit code (which is a number between 0 and 255). For example, the following snippet prints 0:
a=0; a=1 | a=2; echo $a
In ksh (the variants derived from the AT&T code, not pdksh/mksh variants) and zsh, the last item in a pipeline is executed in the parent shell. (POSIX allows both behaviors.) So the snippet above prints 2. You can get this behavior in modern bash by setting the lastpipe
option with shopt -s lastpipe
(in an interactive shell, you also need to disable job control with set +m
).
A useful idiom is to include the continuation of the while loop (or whatever you have on the right-hand side of the pipeline, but a while loop is actually common here) in the pipeline:
cat junk | {
while read var ; do x=55 ; done
echo x=$x
}