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

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

假设我有一个名为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]?,剧本被打断了。有什么问题?

答案1

正如其他人所说,这是因为stdinofsh已被重定向以从管道读取,它没有像通常那样连接到终端。为了解决这个问题,您可以做的一件事是使用/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

效果很好。

答案3

这对于通过管道传输到我的脚本的单个参数有效:

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

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


info test

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

答案4

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

您可能想做这样的事情:

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

相关内容