Changing variables in a BASH while loop

There is an error that I often run in to when working with files and while loops in BASH. Often I have scripts similar to the following, where I cat a file and read it in using a while loop to process variables in the file:

#!/bin/bash
count=0;
cat testfile | while read line
do
    count=$(($count+1));
    echo $count;
done
echo "Total $count";

and using the following test data in testfile as:

cake
pie
thongs

I would expect an output like:

1
2
3
Total 3

But instead I get:

[email protected]:~$ ./t.sh
1
2
3
Total 0

What is happening is due to the | (pipe) bash is forking and creating a new process so any variables we are altering and changing ($count) are being manipulated in the child process, and then lost when the subprocess finishes!

A lot of people I’ve spoken to who have seen this either completely change their code structure to accommodate, or worse, change shell completely to something like KSH!

If we tweak our script ever so slightly, and read the file in at the end of the BASH while loop…

#!/bin/bash
count=0;
while read line
do
    count=$(($count+1));
    echo $count;
done < testfile
echo "Total $count";

everything changes 🙂

[email protected]:~$ ./t.sh
1
2
3
Total 3

and we get the output we expect!

bash Cookbook O'Reilly's Bash Cookbook covers everything you'd ever need to know about Bash, including all the nuances of while loops and powerful scripting techniques. Thoroughly recommended.