2

I'm trying to understand how the bash read command works under the hood. Given that it expects its input to come from standard input, I was surprised to find out that piped input does not work as expected. E.g.

### Pipe scenario

echo "1 2 3" | read -r one two three
echo "one: $one, two: $two, three: $three"
# output:   'one: , two: , three:' 

### Herestring scenario

read -r one two three <<< "1 2 3"
echo "one: $one, two: $two, three: $three"
# output:   'one: 1, two: 2, three: 3'

Can someone explain in what fundamental way do the above two ways of providing input differ from each other (from the point of view of the read command)?



EDIT in response to comments:

I do not want to know "how to work around passing input via a pipe", like the linked questions in the comments. I know how to do that (e.g. I can use a herestring!).

My question is, what is the underlying mechanism that makes read behave differently in the two cases?

Tasos Papastylianou
  • 18,605
  • 2
  • 20
  • 44

1 Answers1

2

The read works, but you need to ask for the values in the same subshell:

echo "1 2 3" | (read -r one two three && echo "one: $one, two: $two, three: $three")

An alternative is

read -r one two three < <( echo "1 2 3")
Walter A
  • 16,400
  • 2
  • 19
  • 36
  • 3
    And another alternative is disabling job control and enabling the lastpipe option – oguz ismail May 06 '20 at 10:13
  • I think this kinda makes sense, but just to clarify. Are you saying that the difference between the two scenarios is that a pipe necessarily creates a new subshell and that the variables are actually bound in the subshell but then lost on return to the base shell? – Tasos Papastylianou May 06 '20 at 10:38
  • 2
    Thank you, having experimented a bit more based on your suggestion, I now understand that pipes opening a subshell to process the piped command is exactly what happens. I am accepting this answer. Btw, regarding the 2nd snippet, I think this is closer to the pipe scenario: `echo "1 2 3" > >(read -r one two three && echo "one: $one, two: $two, three: $three")`. Similarly, I think the top example should be: `echo "1 2 3" | { read -r one two three && echo "one: $one, two: $two, three: $three" }`; piping into a subshell which immediately opens another subshell raises even more questions for me ... – Tasos Papastylianou May 06 '20 at 10:58