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.
-
If 'foo' is a constant, then: a=foo works best. So, this isn't your real problem; it is an over-simplified version of your real problem. – Jonathan Leffler Dec 17 '08 at 15:36
-
this works as expected -- you just expect the wrong thing ;) – Dec 25 '08 at 13:37
8 Answers
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.

- 81,643
- 20
- 123
- 127
-
1You 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 Dec 17 '08 at 14:42
-
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 Dec 17 '08 at 14:54
-
-
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 Dec 17 '08 at 15:02
-
1You 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 Dec 17 '08 at 15:21
-
You're right. It is difficult to find the balance between being too general and too specific. – Diomidis Spinellis Dec 17 '08 at 20:48
-
-
These all make 'foo' assigned to the variable in subshell, but the asker want a variable in parent shell been assigned – cifer Feb 27 '16 at 03:59
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

- 15,072
- 6
- 47
- 98
-
This however doesn't allow me to use the value outside the subshell. – Diomidis Spinellis Dec 17 '08 at 14:41
-
1
-
`read a <<<"foo"; echo $a` also works, and doesn't spawn a subshell & named pipe like the <() – Asfand Qazi Mar 11 '20 at 12:59
I don't think this one has already been given:
a=`echo foo`
echo $a

- 2,622
- 1
- 17
- 15
-
Sure, if you want to read several values you have to do as Bombe suggests. – Arthur Reutenauer Dec 17 '08 at 15:08
Just FYI; in ksh it is working as expected; See also http://kornshell.com/doc/faq.html, Section III (shell programming questions), Q13:
Q13. What is $bar after, echo foo | read bar?
A13. The is foo. ksh runs the last component of a pipeline
in the current process. Some shells run it as a subshell
as if you had invoked it as echo foo | (read bar).
-
In bash if you set the `lastpipe` option (with `shopt -s lastpipe`), it will similarly run the last command of a pipe in the current shell rather than a subshell. – Chris Dodd Jul 13 '14 at 04:51
The | is an inter-process communications operator. So, implicitly, there is a subprocess that must be created to parse and evaluate the expression on one side of it or the other. Bash, and older Bourne shells create a subprocess to the right of the operator (reading from the pipe) which means that any variables set there are only in scope until that process exits (which is at the semicolon in this code example.
zsh and newer versions of Korn shell (at least from '93 on, but possibly even as far back as ksh '88) will create the subprocess on the other side of the pipe (writing into it). Thus they will work in the way that the author of this question intends. ("As expected" is, of course, highly subjective. Understanding the nature of the pipe should lead one to expect this to behave in an implementation specific manner).
I don't know if there is any specific provision in Posix or Spec 1170 or SuS or any other published standard that requires one or the other semantics. However, in practice it's clear that you must not depend on the behavior; though you can easily test for it within your scripts.

- 17,054
- 13
- 68
- 116
-
POSIX doesn't mandate which side(s) of a pipe are to be processed by the main shell. Thus it is undefined behavior. from http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_12 `Additionally, each command of a multi-command pipeline is in a subshell environment; as an extension, however, any or all commands in a pipeline may be executed in the current environment. All other commands shall be executed in the current shell environment.` – jlliagre Mar 27 '12 at 05:28
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

- 18,734
- 5
- 61
- 83
-
2If you're going to use 'set', always use 'set --' to ensure that '-foo' doesn't screw you up. – Jonathan Leffler Dec 17 '08 at 15:35
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.

- 18,734
- 5
- 61
- 83
-
-
Would you care to explain the scopes involved in an answer? – Diomidis Spinellis Dec 26 '08 at 12:33
-
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. – Dec 30 '08 at 18:47
-
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 Jan 02 '09 at 12:30
-
moot point. show me the non-builtin command that can change the environment for any following commands. read _has_ to be built in – Jan 03 '09 at 00:08
-
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. – Jan 03 '09 at 00:10
-
1You'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 Jan 04 '09 at 19:38
read expects input on a new line
while read a; do echo $a; done foo bar ^D
is more or less what you wanted

- 27,109
- 7
- 50
- 78