« How Not to Run an Online Ordering System | Main | Book Review - God's Debris and The Religion War »

Variable Scope in Bash

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)

Jason:

Thanks for the tip. This has being getting on my nerves.

Vinod Guleria:

came across the same problem and this information is really what i needed.

thanks a lot.

Norbert Preining:

See that posting for a better way. Simple replace


cmd | cmd | while read foo ; do
..
done

with

while read foo ; do
...
done < <(cnd | cmd)

But this is bash, but the above page also gives a sh/POSIX compliant way.

Chris:

Thank you for sharing--you solved my headache of the day!!!

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

About

This page contains a single entry from the blog posted on August 2, 2007 3:16 PM.

The previous post in this blog was How Not to Run an Online Ordering System.

The next post in this blog is Book Review - God's Debris and The Religion War.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.34