Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash piping command output into sum loop

Tags:

bash

piping

Getting into bash, I love it, but it seems there are lots of subtleties that end up making a big difference in functionality, and whatnot, anyway here is my question:

I know this works:

total=0
for i in $(grep number some.txt | cut -d " " -f 1); do
    (( total+=i ))
done

But why doesn't this?:

grep number some.txt | cut -d " " -f 1 | while read i; do (( total+=i )); done

some.txt:

1 number
2 number
50 number

both the for and the while loop receive 1, 2, and 50 separately, but the for loop shows the total variable being 53 in the end, while in the while loop code, it just stays in zero. I know there's some fundamental knowledge I'm lacking here, please help me.

I also don't get the differences in piping, for example If I run

grep number some.txt | cut -d " " -f 1 | while read i; echo "-> $i"; done

I get the expected output

-> 1
-> 2
-> 50

But if run like so

while read i; echo "-> $i"; done <<< $(grep number some.txt | cut -d " " -f 1)

then the output changes to

-> 1 2 50

This seems weird to me since grep outputs the result in separate lines. As if this wasn't ambiguous, if I had a file with only numbers 1 2 3 in separate lines, and I ran

while read i; echo "-> $i"; done < someother.txt

Then the output would be printed by the echo in different lines, as expected in the previous example. I know < is for files and <<< for command outputs, but why does that line difference exist?

Anyways, I was hoping someone could shed some light on the matter, thank you for your time!

like image 860
acib708 Avatar asked Jan 28 '15 20:01

acib708


1 Answers

grep number some.txt | cut -d " " -f 1 | while read i; do (( total+=i )); done

Each command in a pipeline is run in a subshell. That means when you put the while read loop in a pipeline any variable assignments are lost.

See: BashFAQ 024 - "I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?"

while read i; echo "-> $i"; done <<< "$(grep number some.txt | cut -d " " -f 1)"

To preserve grep's newlines, add double quotes. Otherwise the result of $(...) is subject to word splitting which collapses all the whitespace into single spaces.

like image 114
John Kugelman Avatar answered Sep 28 '22 17:09

John Kugelman