Bash piping command output into sum loop
Asked Answered
G

1

5

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!

Gravesend answered 28/1, 2015 at 20:1 Comment(2)
As a simple example of the issue in the second question try: a=$'foo\nbar'; echo $a; echo ===; echo "$a; echo ===; cat <<<$a; echo ===; cat <<<"$a".Caracalla
You can also calculate sum of the first column using awk: awk '{a+=$1} END {print a}' some.txtIchneumon
L
10
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; do 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.

Liberalism answered 28/1, 2015 at 20:6 Comment(1)
The other alternative is set +m; shopt -s lastpipe, but that's really muddying the waters.Anissa

© 2022 - 2024 — McMap. All rights reserved.