Reputation: 7807
I use command | awk '{ print $1; }' | while read val ; do
to loop through the command output. Recently I wanted to calculate the sum, and I discovered some strange behaviour in bash
:
content of test.txt
100
200
300
content of test.sh
sum='0'
cat test.txt | awk '{ print $1; }' | while read val ; do
sum=`expr $sum + $val`
echo "sum is now: $sum"
done
echo "FINAL SUM: $sum"
output from executing test.sh
sum is now: 100
sum is now: 300
sum is now: 600
FINAL SUM: 0
the final sum should be 600. What can I do to fix this?
Upvotes: 1
Views: 2818
Reputation: 8398
This strange behaviour is actually caused by the bash pipes
To quote the Bash reference manual
Each command in a pipeline is executed in its own subshell
Think of sum
as a local variable in your while loop, that's why sum
appears not to be set when you go out of the while-loop.
The solutions proposed by the others will work fine.
Upvotes: 0
Reputation: 2357
To expand on what newfurniturey states, but in a way where you can use it with an arbritary input command, not just cat
:
sum='0'
while read val ; do
sum=`expr $sum + $val`
echo "sum is now: $sum"
done < <(cat test.txt | awk '{ print $1 }')
echo "FINAL SUM: $sum"
Replace cat test.txt
with whatever command which outputs the input you require.
Upvotes: 2
Reputation: 38416
The cause is the use of cat
, which spawns another subshell. This means that the sum
variable is incremented in the second subshell, and then goes out of scope (and returns to it's previous value of 0
) when the loop is finished.
Try updating your loop to not use cat
:
sum='0'
while read val ; do
sum=`expr $sum + $val`
echo "sum is now: $sum"
done < test.txt
echo "FINAL SUM: $sum"
If you don't actually need the loop (i.e. - if you're not handling any other column/content-processing), you could use awk
directly and store it's value into the sum
variable:
sum=`awk '{ sum += $1; } END { print sum }' test.txt`;
Upvotes: 2
Reputation: 54392
There's no need for the bash pipe. You can do it all with awk
:
awk '{sum+= $1; printf "The sum is now: %s\n", sum } END { print "FINAL SUM:", sum }' file.txt
Results:
The sum is now: 100
The sum is now: 300
The sum is now: 600
FINAL SUM: 600
Upvotes: 2