6

I want to download a provisioning script that reads some configuration parameters via read, and execute it:

curl http://example.com/provisioning.sh | sh

The problem is, that the read command in the script is call with the -i parameter to provide a default:

read -p "Name: " -i joe name
echo $name

If I download the script, set the +x permission and run it, everything's fine.

If I run it with cat provisioning.sh | sh or sh provisioning.sh, it fails with:

read: Illegal option -i

Why wouldn't read support providing a default if it's run via sh?

But whatever, I'll remove the -i, being left with

read -p "Name: " name
echo $name

Now if I run the script via cat provisioning.sh | sh, it won't do anything. Why is that?

Ubuntu 14.04.

7

When you pipe the output of curl into sh you're making the script text be standard input of the shell, which takes it in as commands to run. After that, there's nothing left to read. Even if it were to try, it wouldn't get anything from the terminal input, because it's not connected to it. The pipe has replaced standard input for the sh process.

The next problem is that read -i is not a POSIX sh feature, but rather an extension supported by bash. Ubuntu uses dash, a minimal POSIX-compliant shell with minimal extensions, as /bin/sh by default. That's why it rejects the -i option specifically (although it does support -p).

If you're using a more capable shell yourself, you can try something like:

bash <(curl http://example.com/provisioning.sh)

which creates a pipe for bash to read the output of curl from and provides it as the script file argument. In this case, the standard input of the script is still connected to the terminal, and read will work (but note the big caveat below the line).


I'll note also that "curl | sh" is generally frowned upon as an obvious security problem, but you know best the situation your script is in.

0

Another, more portable way to accomplish this is to create another file descriptor:

exec 3<>/dev/tty
read -u 3 -p "Gimme some stuff: " stuff

I found that over here: https://superuser.com/questions/834502/possible-to-get-a-bash-script-to-accept-input-from-terminal-if-its-stdin-has-bee

In my case I did a quick find replace in my script so that this would work:

exec 3<>/dev/tty
read_cmd="read -u 3"

$read_cmd -p "name: " my_name
$read_cmd -p "email: " my_email
$read_cmd -s -p "password: " my_password

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.