bash: variable loses value at end of while read loop
I have a problem in one of my shell scripts. Asked a few colleagues, but they all just shake their heads (after some scratching), so I've come here for an answer.
According to my understanding the following shell script should print "Count is 5" as the last line. Except it doesn't. It prints "Count is 0". If the "while read" is replaced with any other kind of loop, it works just fine. Here's the script:
echo "1">input.data echo "2">>input.data echo "3">>input.data echo "4">>input.data echo "5">>input.data CNT=0 cat input.data | while read ; do let CNT++; echo "Counting to $CNT" done echo "Count is $CNT"
Why does this happen and how can I prevent it? I've tried this in Debian Lenny and Squeeze, same result (i.e. bash 3.2.39 and bash 4.1.5. I fully admit to not being a shell script wizard, so any pointers would be appreciated.
See argument @ Bash FAQ entry #24: "I set variables in a loop. Why do they suddenly disappear after the loop terminates? Or, why can't I pipe data to read?" (most recently archived here).
Summary: This is only supported from bash 4.2 and up. You need to use different ways like command substitutions instead of a pipe if you are using bash.
This is kind of a 'common' mistake. Pipes create SubShells, so the while read
is running on a different shell than your script, that makes your CNT
variable never changes (only the one inside the pipe subshell).
Group the last echo
with the subshell while
to fix it (there are many other way to fix it, this is one. Iain and Ignacio's answers have others.)
CNT=0
cat input.data | ( while read
do
let CNT++;
echo "Counting to $CNT"
done
echo "Count is $CNT" )
Long explanation:
- You declare
CNT
on your script to be value 0; - A SubShell is started on the
|
towhile read
; - Your
$CNT
variable is exported to the SubShell with value 0; - The SubShell counts and increase the
CNT
value to 5; - SubShell ends, variables and values are destroyed (they don't get back to the calling process/script).
- You
echo
your originalCNT
value of 0.