Using read -p in a bash script that was executed from pipe

ぃ、小莉子 提交于 2019-12-01 05:57:49

The tty solution works. Test it with this code, for example:

$ date | { read -p "Echo date? " r </dev/tty ; [ "$r" = "y" ] && cat || echo OK ; }
Echo date? y
Sat Apr 12 10:51:16 PDT 2014
$ date | { read -p "Echo date? " r </dev/tty ; [ "$r" = "y" ] && cat || echo OK ; }
Echo date? n
OK

The prompt from read appears on the terminal and read waits for a response before deciding to echo the date or not.

What I wrote above differs from the line below in two key aspects:

read -t 1 __response </dev/tty

First, the option -t 1 gives read a timeout of one second. Secondly, this command does not provide a prompt. The combination of these two probably means that, even though read was briefly asking for input, you didn't know it.

The main reason why this is not working is, as the OP validly indicated,

  • The | <pipe> which is used, sends the standard output from the first command as standard input to the second command. In this case, the first command is

    wget -q -O - http://myscript.sh
    

    which passes a downloaded script via the pipe to its interpreter bash

  • The read statement in the script uses the same standard input to obtain its value.

So this is where it collapses because read is not awaiting input from you but takes it from its own script. Example:

$ cat - <<EOF | bash 
> set -x
> read p
> somecommand
> echo \$p
> EOF
+ read p
+ echo somecommand
somecommand

In this example, I used a here-document which is piped to bash. The script enables debugging using set -x to show what is happening. As you see, somecommand is never executed but actually read by read and stored in the variable p which is then outputted by echo (note, the $ has been escaped to avoid the substitution in the here-document).

So how can we get this to work then?

First of, never pipe to an interpreter such as {ba,k,z,c,tc,}sh. It is ugly and should be avoided, even though it feels the natural thing to do. The better thing to do is to use any of its options:

bash -c string: If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.

$ bash -c "$(command you want to pipe)"

This also works for zsh, csh, tcsh, ksh, sh and probably a lot of others.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!