Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Why do I get different values for $x from the snippets below?


echo fred>junk ; while read var ; do x=55 ; done <junk
echo x=$x 
#    x=55 .. I'd expect this result

cat junk | while read var ; do x=55 ; done
echo x=$x 
#    x=1 .. but why?

echo fred | while read var ; do x=55 ; done
echo x=$x 
#    x=1  .. but why?
share|improve this question

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 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.

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 
share|improve this answer
Thanks Gilles .. That a=0; a=1 | a=2 gives a very clear picture.. and not only of the localization of internal state, but also that a pipeline doesn't actually need to send anything through the pipe (other than the exit code(?).. In itself that is an interesting insight into a pipe ... I did manage to get my script running with < <(locate -ber ^\.tag$), thanks to the original slightly unclear answer and geekosaur and glenn jackman's comemnts.. I was initially in a dilemma about accepting the answer, but the nett result was pretty clear, especially with jsbillings follow-up comment :) – Peter.O Mar 24 '11 at 12:31
it feels like I piped into a function, so I moved some variables and tests to inside it and it worked great, thx! – Aquarius Power Aug 1 '14 at 21:04

You're running into a variable scope issue. The variables defined in the while loop that is on the right side of the pipe have their own local scope context, and changes to the variable will not be seen outside of the loop. The while loop is essentially a subshell which gets a COPY of the shell environment, and any changes to the environment are lost at the end of the shell. See this StackOverflow question.

UPDATED: I neglected to point out the important fact that the while loop with it's own subshell was due to it being the endpoint of a pipe, I've updated that in the answer.

share|improve this answer
@jsbillings.. Okay, that explains the two last snippets, but it doesn't explain the first, where the value of $x set in the loop, is carried on as 55 (beyond the scope of the 'while' loop) – Peter.O Mar 23 '11 at 14:39
@fred.bear: It's running the while loop as the tail end of a pipeline that throws it into a subshell. – geekosaur Mar 23 '11 at 14:44
This is where bash process substitution comes into play. Instead of blah|blah|while read ..., you can have while read ...; done < <(blah|blah) – glenn jackman Mar 23 '11 at 15:01
@geekosaur: thanks for filling in the details that I neglected to include in my answer. – jsbillings Mar 23 '11 at 15:31
-1 Sorry but this answer is just wrong. It explains how this stuff works in many programming languages but not in the shell. @Gilles, below, got it right. – jpc Mar 24 '11 at 3:13
set -x

# prepare test data.
mkdir -p ~/test_var_global
cd ~/test_var_global
echo "a"> core.1
echo "b"> core.2
echo "c"> core.3


coreFiles=$(find . -type f -name "core*")
while read -r file;
  # perform computations on $i
done <<EOF

echo $var

+ echo 3

it can work.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.