From: Lew Pitcher on
On July 16, 2010 17:43, in comp.unix.shell, ceaus(a)nospam wrote:

> Hi,
>
> What gives? Is this a bash bug?

Nope. It's a sign that you don't understand how pipes work in bash.

> Note the different output for 'while' and 'for'
>
> ------------------------------------------------------------
> function foo() {
> echo Foo | while read x
> do
> var=2
> done
> }
> var=1
> echo $var # prints 1
> foo
> echo $var # prints 1

the right-hand-side of the pipe runs in a subshell. This means that the
while read x
do
var=2
done
portion of the code has no write access to the environment variables on the
echo Foo
side of the pipe. Your commandline shell resides on the
echo Foo
side of the pipe.

What happens is this:
In your main shell, you set
var=1
You then run function foo, which, in your main shell performs an
echo Foo
piping the stdout from echo into the stdin of a 2nd process. The main shell
then waited for that 2nd process to finish.

Now, the 2nd process inherited your
var=1
and changed *it's inherited value* to 2. This second process then
terminated, and it's environment was discarded. Including the changed value
of var, which (as a changed value) only existed in this second process.

When the 2nd process finished, your commandline shell reaped it's status,
and gave you another prompt. you then
echo $var
and find that, in this first process, var had not changed. That's because it
had changed *only in the 2nd process*, and was discarded.

> ------------------------------------------------------------
> function foo() {
> for x in Foo
> do
> var=2
> done
> }
> var=1
> echo $var # prints 1
> foo
> echo $var # prints 2

Here, because no pipe is involved, no second process is started. The entire
command is executed within the process that is your commandline shell. This
means that the script given above has write access to environment variables
in the commandline shell, and you see that the script has changed one of
the environment variable values.

> ------------------------------------------------------------
>
>
> bash = GNU bash, versie 4.0.35(1)-release (x86_64-suse-linux-gnu)
>
> Ceaus


HTH
--
Lew Pitcher
Master Codewright & JOAT-in-training | Registered Linux User #112576
Me: http://pitcher.digitalfreehold.ca/ | Just Linux: http://justlinux.ca/
---------- Slackware - Because I know what I'm doing. ------


From: Jon LaBadie on
Ceaus wrote:
> Hi,
>
> What gives? Is this a bash bug?
> Note the different output for 'while' and 'for'
>
> ------------------------------------------------------------
> function foo() {
> echo Foo | while read x
> do
> var=2
> done
> }
> var=1
> echo $var # prints 1
> foo
> echo $var # prints 1
> ------------------------------------------------------------
> function foo() {
> for x in Foo
> do
> var=2
> done
> }
> var=1
> echo $var # prints 1
> foo
> echo $var # prints 2
> ------------------------------------------------------------
>

Not a bug. When a control structure is redirected, bash
runs the code in a child process. Therefore, in the
parent, the variable is unchanged.

Change your second function to:

echo Foo | for x in Foo

And it too will print out 1&1.

jl
From: Sven Mascheck on
Jon LaBadie wrote:

>> [...]
>> echo Foo | while read x

> When a control structure is redirected, bash
> runs the code in a child process.

"redirection" (instead of "pipeline") better describes
a different issue, which in fact also means a subshell,
in traditional Bourne shells (only).

$ i=global
$ for i in local; do :; done > /dev/null
$ echo $i
global

(an example with "while read x" would be more subtle,
because the variable becomes empty with an EOF.)