Jose
Jose

Reputation: 3558

Reading user input in a script piped to the interpreter

I want to get the user input in a bash script that is first downloaded via wget (proxied in the examble below using cat) and then fed into bash through stdin. I thought it would be straightforward but it doesn't work.

The script (in a file called gistfile2.sh):

#!/bin/bash

echo -n "Enter path: "
read path
echo $path

How I run it:

root@precise32:/tmp# cat (or wget -qO-) gistfile2.sh | bash 
Enter path: root@precise32:/tmp#

Any ideas?

Upvotes: 0

Views: 64

Answers (1)

Charles Duffy
Charles Duffy

Reputation: 295815

First, let's look at how pipelines work:

When

foo | bar

runs bar, bar's stdin is connected to foo's stdout. This means that bar has no way of knowing what stdin descriptor it would have inherited were it not for the pipeline overriding it. This information is lost, and cannot be recovered.

However, in practice, one can often guess: If stderr is still a TTY handle, that can be opened for stdin; and if the operating system provides a facility such as /dev/tty to get a handle on the process's controlling tty, all the better.


So:

If your stdin (FD 0) has been overridden by a pipeline, you can try to read from /dev/tty. This is not by any means guaranteed to work, and is heavily platform-dependent functionality -- but if it doesn't work, then you'll be falling back on other hackery such as hoping that stderr hasn't been redirected.

To read from /dev/tty:

read path </dev/tty || { rc=$?; echo "Unable to read from TTY" >&2; exit "$rc"; }

To read from stderr (assuming that it's connected to a read/write FD going to your TTY -- a big assumption):

read path <&2 || { rc=$?; echo "stderr not attached to a TTY?" >&2; exit "$rc"; }

Upvotes: 2

Related Questions