So, I've been doing a lot of Bash scripting lately -- cranking out an upgrade utility so we can more quickly migrate mail servers on our new data replication software, Fenbu, being chief among recent tasks. There have been several smaller projects too -- tasks that are just more simply and quickly accomplished via direct Linux commands. I had never had issues with variable scope in Bash before, but I discovered something interesting/annoying today. I was trying to increment a counter variable from within a do/while loop, fed via a pipe from a search for IMAP processes. Initially, I had:
COUNTER=0
ps -Af | grep "imap \[$1 " | grep -v grep | awk '{print $2}' |
while IFS='\n' read PID
do
# Don't do anything with $PID yet for the sake of testing
COUNTER=`$(($COUNTER + 1))`
echo "$COUNTER"
done
echo "Found $COUNTER IMAP Processes"
The above will end up echoing something like:
1
2
3
Found 0 IMAP Processes
It turns out that this is because pipes cause Bash to perform operations within sub-shells, and variables are specific to whatever sub-shell they're called in. So, once we leave the do/while loop, $COUNTER will again evaluate to 0. Lame!
A little bit of tinkering later, and I found a work-around:
COUNTER=0
PIDS=`ps -Af | grep "imap \[$1 " | grep -v grep | awk '{print $2}'`
for PID in $PIDS
do
# Don't do anything with $PID yet for the sake of testing
COUNTER=$(($COUNTER + 1))
done
echo "Found $COUNTER IMAP Processes"
There are no pipes going to the loop itself, so there are no sub-shells to worry about. Problem solved! Though it took a bit longer than I would have liked to figure that one out.
Comments (5)
This page may be interesting for you:
http://nion.modprobe.de/blog/archives/531-Altering-a-variable-outside-the-scope-of-a-loop-influenced-by-a-subshell.html
Posted by Anonymous | October 3, 2007 12:36 PM
Posted on October 3, 2007 12:36
Thanks for the tip. This has being getting on my nerves.
Posted by Jason | February 22, 2008 4:31 PM
Posted on February 22, 2008 16:31
came across the same problem and this information is really what i needed.
thanks a lot.
Posted by Vinod Guleria | May 15, 2008 3:46 AM
Posted on May 15, 2008 03:46
See that posting for a better way. Simple replace
with
But this is bash, but the above page also gives a sh/POSIX compliant way.
Posted by Norbert Preining | November 11, 2008 7:47 AM
Posted on November 11, 2008 07:47
Thank you for sharing--you solved my headache of the day!!!
Posted by Chris | April 14, 2009 3:42 PM
Posted on April 14, 2009 15:42