Chriszuma
Chriszuma

Reputation: 4558

Why is this For loop not not the same as this While loop in Bash?

Okay, so I'm trying to parse a file that outputs in columns what I need in rows, and I can't get this while loop to work, and I'm really stumped.

Interestingly, doing almost the exact same thing with a for loop does work. Can someone please explain what's going on here?

This...

e=""
for f in 1 2 3
do 
    echo $f 
    e="$e.$f"
done
echo $e

Outputs:

1
2
3
.1.2.3

But this...

e=""
echo "1
2
3" | while read f
do 
    echo $f 
    e="$e.$f"
done
echo $e

Outputs:

1
2
3

Clearly both loops have 1, 2, or 3 in $f when they get to e="$e.$f", so what the heck is going on that the second one doesn't work?

Upvotes: 3

Views: 301

Answers (4)

Philip Couling
Philip Couling

Reputation: 14915

I can't tell you how many times I've had to pull this bug out of someone's code.

Basically the pipe generates a forked process. e is set in the forked process, but when that terminates (at the end of the wile loop) the parent process does not know that e has changed. In short, never set a variable in a command involving a pipe. It will not be set when the pipe finishes. Shorter... Don't use pipes if they can be avoided.

e=''
while read f
do
  echo $f
  e="$e.$f"
done << EOF
1
2
3
EOF
echo $e

Upvotes: 8

Jo So
Jo So

Reputation: 26531

As the while read... done is part of a pipeline, it gets its own forked process image just like other binaries (that's called a subshell). (The confusing part is that it's written in shell, but nevertheless it isn't executed as part of the current shell process.) That's why the variables won't get updates.

as a workaround, you can alway do something like this (uses bashisms):

while read line
do
    blablabla
done < <(echo "1 2 3")

Here, you avoid setting up a pipeline, and so the while...done shell code is executed as part of your current script.

Upvotes: 3

Kent
Kent

Reputation: 195219

pipe will be executed in a sub-shell. so the variable e was not changed after the piped command execution.

Upvotes: 3

Furbeenator
Furbeenator

Reputation: 8285

I'm guessing this is a difference in the scope of the variables. When you pipe your echo statement into the while statement, the global e is not in its scope, so it prints the f and sets a new variable e. When the while loop exits, the global e has not changed. Hence your statement echo $e prints nothing.

Upvotes: 1

Related Questions