In my experience, subshells were the most confusing aspect of Unix.

From Chapter 28 in The Linux Command Line:

YOU CAN’T PIPE READ

While the read command normally takes input from standard input, you cannot do this:

echo "foo" | read

We would expect this to work, but it does not. The command will appear to succeed, but the REPLY variable will always be empty. Why is this? The explanation has to do with the way the shell handles pipelines. In bash (and other shells such as sh), pipelines create subshells. These are copies of the shell and its environment that are used to execute the command in the pipeline. In our previous example, read is executed in a subshell. Subshells in Unix-like systems create copies of the environment for the processes to use while they execute. When the processes finish, the copy of the environment is destroyed. This means that a subshell can never alter the environment of its parent process. read assigns variables, which then become part of the environment. In the example above, read assigns the value foo to the variable REPLY in its subshell’s environment, but when the command exits, the subshell and its environment are destroyed, and the effect of the assignment is lost.

Using here strings is one way to work around this behavior. Another method is discussed in Chapter 36.

That method is process substition.

Examples

Using herestrings

read <<< echo "foo"
echo $REPLY

Using process substition

Using process substitution

while read line; do echo \# $line; done < <(cat --help)

This doesn’t work

while read line; do echo \# $line; done < $(cat --help)

because it is a (here)string, not a file. So use <<< to redirect it:

while read -r line; do echo \# $line; done <<< $(cat --help)

The following is another method (but it creates a file):

cat --help > cathelp
while read -r line; do echo \# $line; done < cathelp
rm cathelp

Redirection syntax

For more info on redirection syntax, see the this redirection cheatsheet.