1

I have two scripts:

install.sh

#!/usr/bin/env bash

./internal_install.sh

internal_install.sh

#!/usr/bin/env bash
set -x
while true; do
    read -p "Hello, what's your name? " name
    echo $name
done

When I run ./install.sh, all works as expected:

> ./install.sh 
+ true
+ read -p 'Hello, what'\''s your name? ' name
Hello, what's your name? Martin
+ echo Martin
Martin
...

However, when I run with cat ./install.sh | bash, the read function does not block:

cat ./install.sh | bash
+ true
+ read -p 'Hello, what'\''s your name? ' name
+ echo

+ true
+ read -p 'Hello, what'\''s your name? ' name
+ echo

...

This is just a simplified version of using curl which results in the same issue:

curl -sl https://www.conteso.com/install.sh | bash

How can I use curl/cat to have blocking read in the internal script?

Cyrus
  • 84,225
  • 14
  • 89
  • 153
Tibor Takács
  • 3,535
  • 1
  • 20
  • 23
  • Piping unknown data directly to `bash` is a security risk. Better to save the output of `curl` to a file where you can verify what you've downloaded (visually, comparing its hash to a known hash, etc) before executing it. – chepner Oct 21 '21 at 15:54

1 Answers1

5

read reads from standard input by default. When you use the pipe, standard input is the pipe, not the terminal.

If you want to always read from the terminal, redirect the read input to /dev/tty.

#!/usr/bin/env bash
set -x
while true; do
    read -p "Hello, what's your name? " name </dev/tty
    echo $name
done

But you could instead solve the problem by giving the script as an argument to bash instead of piping.

bash ./install.sh

When using curl to get the script, you can use process substitution:

bash <(curl -sl https://www.conteso.com/install.sh)
Barmar
  • 741,623
  • 53
  • 500
  • 612