如何从管道读取用户输入?


9

假设我有一个文件confirmation.sh,其内容如下:

#!/bin/bash
echo -n "Are you sure [Y/n]? "
read line
case "$line" in
    n|N) echo "smth"
        ;;
    y|Y) echo "smth"
        ;;
esac

并且我想通过以下方式运行此脚本:

cat confirmation.sh | sh

我看到Are you sure [Y/n]?了,脚本被打断了。有什么问题?


2
你有/bin/bash在爆炸线,但你使用一个.sh扩展,并尝试管脚本sh。没问题,因为您拥有的代码与两者兼容,但值得指出。
Graeme 2014年

Answers:


8

正如其他人所说,这是因为stdinof sh已被重定向到管道中以进行读取,它没有像通常那样连接到终端。要解决此问题,您可以做的一件事是使用/dev/tty强制脚本从终端读取的方法。例如:

#!/bin/sh

read -p "Are you sure [Y/n]?" line </dev/tty
case "$line" in
  y|Y) echo "confirmed"
    ;;
  *) echo "not confirmed"
    ;;
esac

通常,仅当您特别想阻止人们编写输入脚本时,您才需要这样做,例如:

echo Y | sh confirmation.sh

即使用户可能希望它Y在提示符下自动输入,仍会从终端读取该信息。对于期望密码执行此操作的程序,这很常见。


2
sh 3<<CONFIRM /dev/fd/3
    $(cat ./confirmation.sh)
CONFIRM

sh 3<./confirmation.sh /dev/fd/3

注意:感谢@Graeme在上述两个示例中纠正我...

如果保持stdin清楚,这样做会容易得多。

2<./confirmation.sh . /dev/stderr

或者,由于终端的0 1 2都是相同的文件,因此只需添加:

read line <&2

和你的

cat ./confirmation.sh | sh

效果很好。


除了上一个,我无法让其他任何一个工作。
Graeme 2014年

很奇怪,他们都为我工作。。。同时... 也许大胆评论?我不喜欢散布错误信息。
mikeserv 2014年

@Graeme-不确定我为什么第一次错过它-可能发誓我同时测试了它们-但是前两个确实需要进行更改才能正确运行。谢谢。
mikeserv 2014年

看起来不错,当我从中获取信息时,它仍然不会从终端读取stderr。我也许应该,但我不明白为什么。
Graeme 2014年

@Graeme-很奇怪-我用dash sh zsh和bash尝试过。所有工作。
mikeserv 2014年

0

这适用于将一个参数传递给我的脚本的情况:

if [ -p /dev/stdin ]; then set -- "$( cat )"; fi

然后,我可以在位置参数($1)中访问管道数据,该位置参数与其他脚本工作方式兼容。


来自info test

[ -p /dev/stdin ]-p FILE如果FILE存在且为命名管道,则为True。


-1

简短的答案是你不能。管道将stdout重定向到stdin,因此您无法运行交互式脚本,因为您已经将第一条命令的输出重定向为pipe语句中的第二条命令的输入。

您可能正在寻找做这样的事情:

cat confirmation.sh > ask.sh && sh ask.sh

您仍然可以运行交互式脚本,请参阅我的答案。另外,为什么不sh confirmation.sh呢?
Graeme 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.