62

When I try to use the read command in Bash like this:

echo hello | read str
echo $str

Nothing echoed, while I think str should contain the string hello. Can anybody please help me understand this behavior?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Determinant
  • 3,886
  • 7
  • 31
  • 47
  • Related: [Looping through the content of a file in Bash](https://stackoverflow.com/q/1521462/6862601) and [BashFAQ/024 - I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates?](http://mywiki.wooledge.org/BashFAQ/024). – codeforester Oct 18 '18 at 00:01

9 Answers9

63

The read in your script command is fine. However, you execute it in the pipeline, which means it is in a subshell, therefore, the variables it reads to are not visible in the parent shell. You can either

  • move the rest of the script in the subshell, too:

    echo hello | { read str
      echo $str
    }
    
  • or use command substitution to get the value of the variable out of the subshell

    str=$(echo hello)
    echo $str
    

    or a slightly more complicated example (Grabbing the 2nd element of ls)

    str=$(ls | { read a; read a; echo $a; })
    echo $str
    
Bruno
  • 401
  • 5
  • 13
jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • Awesome examples! I did have to slightly tweak it on OSX by putting spaces inside the curly braces, and a semi-colon after the echo, like this: str=$(ls | { read a; read a; echo $a; }) – John Lehmann Nov 26 '12 at 19:12
  • @javadba: It is not required twice. The given example returns the *second* item from `ls`. With a single `read a` you would get the first item. Don't ask me why the answerer chose this example. – Fritz May 02 '17 at 11:39
  • To read three (or more) IFS-separated columns into variables one can do 'read VAR_1 VAR_2 VAR_3`. – Krzysztof Jabłoński Mar 02 '18 at 19:07
  • keeps biting me. Should be documented in the man page.. – eMPee584 Mar 20 '21 at 13:50
43

Other bash alternatives that do not involve a subshell:

read str <<END             # here-doc
hello
END

read str <<< "hello"       # here-string

read str < <(echo hello)   # process substitution
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 2
    You can use a process for the here string as well such as `read A B C <<< $(echo 'aaa bbb ccc')` – maxpolk Feb 07 '17 at 15:46
8

Typical usage might look like:

i=0
echo -e "hello1\nhello2\nhello3" | while read str ; do
    echo "$((++i)): $str"
done

and output

1: hello1
2: hello2
3: hello3
Shizzmo
  • 16,231
  • 3
  • 23
  • 15
3

The value disappears since the read command is run in a separate subshell: Bash FAQ 24

l0b0
  • 55,365
  • 30
  • 138
  • 223
2

To put my two cents here: on KSH, reading as is to a variable will work, because according to the IBM AIX documentation, KSH's read does affects the current shell environment:

The setting of shell variables by the read command affects the current shell execution environment.

This just resulted in me spending a good few minutes figuring out why a one-liner ending with read that I've used a zillion times before on AIX didn't work on Linux... it's because KSH does saves to the current environment and BASH doesn't!

RAKK
  • 506
  • 5
  • 10
1

I really only use read with "while" and a do loop:

echo "This is NOT a test." | while read -r a b c theRest; do  
echo "$a" "$b" "$theRest"; done  

This is a test.
For what it's worth, I have seen the recommendation to always use -r with the read command in bash.

Sᴀᴍ Onᴇᴌᴀ
  • 8,218
  • 8
  • 36
  • 58
JackDR
  • 11
  • 2
1

You don't need echo to use read

 read -p "Guess a Number" NUMBER
Ege
  • 515
  • 5
  • 14
-1

Another alternative altogether is to use the printf function.

printf -v str 'hello'

Moreover, this construct, combined with the use of single quotes where appropriate, helps to avoid the multi-escape problems of subshells and other forms of interpolative quoting.

jxqz
  • 119
  • 1
  • 8
  • If that's all you're trying to achieve, you might as well do `str='hello`. I think the point here is to capture the output of a command into a variable. – mc0e Mar 01 '16 at 03:44
-6

Do you need the pipe?

echo -ne "$MENU"
read NUMBER
mmrtnt
  • 382
  • 1
  • 8