在从管道执行的Bash脚本中使用read -p

5
我很抱歉 - 我并不完全了解我所询问的背后的思想,以至于不知道为什么它不能工作(我不知道我需要学习什么)。我首先在Stack Exchange上搜索答案 - 我找到了一些似乎可能相关的信息,但是没有解释这些概念得足够清晰,以至于我能够构建一个可行的解决方案。我一直在搜索Google,但没有找到任何描述正在发生的事情的确切信息,以便我理解。任何指向可能帮助我理解正在发生的事情的背景概念的方向都将不胜感激。

在从管道执行的bash脚本中是否有可能获取用户输入?

例如:

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

在脚本中:
read -p "Do some action (y/n): " __response
if [[ "$__response" =~ ^[Yy]$ ]]; then
   echo "Performing some action ..."
fi

据我了解,这个方法不起作用是因为read尝试从stdin读取输入,而bash脚本当前正在“通过该管道执行”(我确定有一种更精确技术的描述方法,但我不知道如何描述)。

我找到一个解决方案,建议使用:

read -t 1 __response </dev/tty

然而,这也不能起作用。
如有关于需要理解的概念、为什么它不起作用或解决方案的任何说明,将不胜感激。
2个回答

4

tty 的解决方案有效。例如,使用以下代码进行测试:

$ 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
< p > read 命令会在终端上显示提示信息,等待用户响应后再决定是否输出日期。< /p > < p > 与下面这行代码相比,我上面写的有两个关键不同之处:< /p >
read -t 1 __response </dev/tty

首先,选项-t 1read设置了一秒的超时时间。其次,该命令没有提供提示符。这两个组合可能意味着,即使read短暂要求输入,您也不知道它。

谢谢。运行得非常完美。 - nfarrar

2
主要原因是这不起作用,正如OP所指出的那样。
  • 使用的| <pipe>将第一个命令的标准输出作为第二个命令的标准输入发送。在这种情况下,第一个命令是

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

    它通过管道将下载的脚本传递给其解释器bash

  • 脚本中的read语句使用相同的标准输入来获取其值。

所以这就是它崩溃的地方,因为read不等待您的输入,而是从其自己的脚本中获取输入。例如:

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

在这个例子中,我使用了一个here-document,它被管道传输到bash。脚本使用set -x启用调试以显示正在发生的事情。正如您所看到的,somecommand从未被执行,而是由read读取并存储在变量p中,然后由echo输出(请注意,$已被转义以避免在此处文档中进行替换)。

那么我们该如何让它工作呢?

首先,永远不要将其管道传输到解释器,例如{ba,k,z,c,tc,}sh。这很丑陋,应该避免,即使它感觉是自然的事情。更好的做法是使用任何选项:

bash -c string如果存在-c选项,则从字符串中读取命令。如果字符串后面有参数,则将它们分配给位置参数,从$0开始。

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

这对于zshcshtcshkshsh以及可能还有很多其他的都适用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接