I'm not looking for work-arounds or solutions for the issue. I'm fine with it not working like that in bash. I just don't understand why it doesn't work.

I'm looking for an in-depth answer why the following script doesn't work. All previous internet search results, including posts from unix.stackexchange.com, couldn't really clear this up completely. It has something to do with read reading from stdin which doesn't work because stdin is already "taken" (?) by cat feeding bash via the pipe?

Example bash script test.sh:

echo "Please say name:"
read NAME
echo "Hello $NAME"

Method 1 calling the script with bash test.sh:

$ bash test.sh
Please say name:
Hello XYZ

Method 2 running the script via piping to bash:

$ cat test.sh | bash
Please say name:

So the script immediately returns to the prompt, without waiting for input or even printing the second line.

  • It waited for and received input echo "Hello $NAME", then terminated. – Michael Homer Apr 5 '19 at 21:04

You did read from stdin with read, but what you read was the next line of standard input - namely echo "Hello $NAME". After reading that line, there was no more input and so no further commands to execute, and the script was over.

There is only one standard input stream, and you're trying to use it for both code and data. This is the same as how an interactive bash session reads commands from your typing, as well as read responses, as well as whatever any other commands you run want to use standard input for.

You can see this happening if we add an extra line to the end of the script:

echo "Please say name:"
read NAME
echo "Hello $NAME"
printf 'name=%s\n' "$NAME"

This both provides a further command to see the script continue execution, and shows us what was read into NAME:

Please say name:
name=echo "Hello $NAME"

You can see that the variable holds verbatim what was written in the script file - no variable interpolation, execution, or expansion has happened.

If you want to read from the terminal, it is possible. The simplest way that's likely to work is to read from standard output instead of standard input (!), which is presumably connected to the TTY:

read NAME <&1

This will wait for me to type something, and then move on to the rest of the program. You could also use /dev/tty or $(tty).

| improve this answer | |
  • arguably read var </dev/tty would be better than assuming that stdout is connected to the contrilling tty. – mosvy Apr 6 '19 at 6:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.