noname
noname

Reputation: 3

Adding numbers with a while loop using piped output

So i am running a randomfile that can receive several arguments ($1 and $2) not shown, and then does something with the argument passed...

with the 3rd argument, i am supposed to search for $3 (or not $3) in file1 and add number of instances of this to file2 ...

this works fine:

cat file1 | grep $3 | wc -l | while read line1; do echo $3 $line1 > file2; done

cat file1 | grep -v $3 | wc -l | while read line2; do echo not $3 $line2 >> file2; done

Now I am trying to read file2 that is holding the instances of the search, i want to get the numbers in the file, get the sum, to then append to file2. So, for example, if $3 was "baby":

file2 would contain-

baby 30
not baby 20

and then i want to get the sum of 20 and 30 and append to that same file2, so that it looks like-

baby 30
not baby 20
total 50

This is what i have at the moment:

cat file2 | grep -o '[0-9]*' | while read num ; do sum=$(($sum + $num));echo "total $sum" >> file2; done

my file2 ends up with two lines for totals, where one of them is what i need-

baby 30
not baby 20
total 30
total 50

What did I miss here?

Upvotes: 0

Views: 78

Answers (2)

Theo
Theo

Reputation: 1633

This is happening because your echo is within your for loop.

The obvious solution would be to move this outside your for loop, but if you try this you will find that $sum is not set, this is because the while loops and pipes are actually spawned as their own processes. You can solve this by using braces ({}) to group your commands:

cat file2 | grep -o '[0-9]*' | { while read num ; do sum=$(($sum + $num)); done; echo "total $sum" >> file2; }

Other answers do point out better ways of doing this, but this hopefully helps you understand what is happening.

Upvotes: 1

cat file1 | grep $3 | wc -l | while read line1; do echo $3 $line1 > file2; done

If you want to count the instances of $3, you can use the option -c of grep, avoiding a pipe to wc(1). Moreover, it would be better to quote the $3. Finally, you don't need a loop to read the count (either from wc or grep): it is a single line! So, your code above could be written like this:

count=$(grep -c "$3" file1)
echo $count $3 >file2

The second grep would be just the same as before:

count=$(grep -vc "$3" file1)
echo $count $3 >>file2

Now you should have the intermediate result:

30 baby
20 not baby

Note that I reversed the two terms, count and pattern; this is because we know that the count is a single word, but the pattern could be more words. So writing first the count, we have a well defined format: "count, then all the rest".

The third loop can be written like this:

while read num string; do 
  # string is filled with all the rest on the line
  let "sum = $sum + $num"
done < file2
echo "$sum total" >> file2

There are other ways to sum up the total; if needed, you could also reverse again the terms of the final file, as was your original - it could be done by using another file again.

Upvotes: 0

Related Questions