tags:

views:

566

answers:

7

I could replicate the problem with various shells under FreeBSD, GNU/Linux, and Solaris. It had me head-scratching for more than an hour, so I decided to post the question here.

+8  A: 

Due to the piping the read is executed in its own subshell.

echo foo | while read a; do echo $a; done

will do what you expect it to.

Bombe
You know, I had that exact problem, came up with that exact response (a while loop that only executed once) and never realized why it worked. Thanks.
Paul Tomblin
I still can't use it, because $a looses its value outside the while block!echo foo | while read a; do echo $a; done ; echo $a
Diomidis Spinellis
@Diomidis, So put whatever you need to do with $a in the loop.
Paul Tomblin
Yeah, I know I can do that, but this distorts the code, and it doesn't scale. I posted another answer of an approach, which I ended up using.
Diomidis Spinellis
You should have told us more about your problem. You original question was just “why doesn’t this work” (and has been answered) but we could probably have helped you a lot better if we knew more about your problem.
Bombe
You're right. It is difficult to find the balance between being too general and too specific.
Diomidis Spinellis
echo foo | { read a; echo $a; } does the same thing without while.
hop
+4  A: 

alternative:

echo foo | (read a ; echo $a)

Edit:

If you need $a outside the subshell, you have to reverse the commands:

read a < <(echo foo); echo $a

this way the read is executed in the current process

Wimmel
This however doesn't allow me to use the value outside the subshell.
Diomidis Spinellis
Nice, but note that <() is not POSIX syntax.
daf
A: 

read expects input on a new line

while read a; do echo $a; done
foo
bar
^D

is more or less what you wanted

James Anderson
A: 

According to Kernighan and Pike's The Unix Programming Environment (p. 159) "none of the shell built-in commands (as opposed to the control flow primitives, like for) can be redirected with > and <", and "this might be described as a bug in the shell". This seems to explain a) why code like

ls *.c |
while read file
do
   echo $a
done

invariably works without a problem, and b) the inconsistency that redirecting from a file works.

Diomidis Spinellis
no, it doesn't explain it. you problem is simply a scoping issue.
hop
Would you care to explain the scopes involved in an answer?
Diomidis Spinellis
in bash, the pipe causes the following command to be executed in a subshell. the environment variable you set with read is destroyed as soon as this subshell exits (at the ; between read and echo) and thus is not available to the next command.
hop
Bombe already explained this in his answer.
hop
That's a good explanation, if changed into "the pipe causes the following *built-in* command to be executed in a subshell". (External commands that are part of a pipe are definitely not run in a subshell.)
Diomidis Spinellis
moot point. show me the non-builtin command that can change the environment for any following commands. read _has_ to be built in
hop
btw, the man page states: "Each command in a pipeline is executed as a separate process (i.e., in a subshell)." Nothing about a distinction between built-in or not built-in there.
hop
You're right about non-builtin commands changing the environment, and also right about the man page; I was not aware of this, I thought commands in the pipeline were spawned off directly from the parent shell.
Diomidis Spinellis
+3  A: 

I don't think this one has already been given:

a=`echo foo`
echo $a
Arthur Reutenauer
This works only for reading a single value.
Diomidis Spinellis
Sure, if you want to read several values you have to do as Bombe suggests.
Arthur Reutenauer
A: 

I came up with a solution that doesn't hide the variable values in a subshell, and that can also work with multiple values.

set `echo foo bar`
A=$1
B=$2
Diomidis Spinellis
If you're going to use 'set', always use 'set --' to ensure that '-foo' doesn't screw you up.
Jonathan Leffler
A: 

Just FYI; in ksh it is working as expected; See also http://kornshell.com/doc/faq.html, Q13

Wimmel
It also works in zsh.
Jonas Due Vesterheden