Reputation: 4558
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
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
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
Reputation: 195219
pipe will be executed in a sub-shell. so the variable e was not changed after the piped command execution.
Upvotes: 3
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