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:
idimmu@boosh:~$ ./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 🙂
idimmu@boosh:~$ ./t.sh 1 2 3 Total 3
and we get the output we expect!
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.
Good tip! thank you for the explanation =)