为什么在将脚本管道传输到 bash 时不能使用 `read` 从 `stdin` 中读取?

fin*_*oot 5 scripting bash pipe stdin read

我不是在寻找该问题的变通方法或解决方案。我很好,它在bash. 我只是不明白为什么它不起作用。

我正在寻找为什么以下脚本不起作用的深入答案。所有以前的互联网搜索结果,包括来自 unix.stackexchange.com 的帖子,都无法真正完全清除这一点。它与read读取有关,stdin它不起作用,因为stdin已经通过管道cat喂食“采取”(?)bash

示例 bash 脚本test.sh

echo "Please say name:"
read NAME
echo "Hello $NAME"
Run Code Online (Sandbox Code Playgroud)

方法 1 调用脚本bash test.sh

$ bash test.sh
Please say name:
XYZ
Hello XYZ
$
Run Code Online (Sandbox Code Playgroud)

方法 2 通过管道运行脚本bash

$ cat test.sh | bash
Please say name:
$
Run Code Online (Sandbox Code Playgroud)

因此脚本立即返回到提示,无需等待输入甚至打印第二行。

Mic*_*mer 9

确实使用读取了标准输入read,但是您读取的是标准输入的下一行 - 即echo "Hello $NAME". 读完那行后,没有更多的输入,所以没有更多的命令要执行,脚本就结束了。

目前只有一个标准输入流,并且你想使用它的代码和数据。这与交互式bash会话从您的输入、read响应以及您运行的任何其他想要使用标准输入的命令中读取命令的方式相同。

如果我们在脚本末尾添加额外的一行,您可以看到这种情况发生:

echo "Please say name:"
read NAME
echo "Hello $NAME"
printf 'name=%s\n' "$NAME"
Run Code Online (Sandbox Code Playgroud)

这两者都提供了一个进一步的命令来查看脚本继续执行,并向我们展示了读入的内容NAME

Please say name:
name=echo "Hello $NAME"
Run Code Online (Sandbox Code Playgroud)

您可以看到该变量逐字保存了脚本文件中写入的内容 - 没有发生变量插值、执行或扩展。


如果你想read从终端,这是可能的。可能有效的最简单方法是从标准输出而不是标准输入 (!) 中读取,它可能连接到 TTY:

read NAME <&1
Run Code Online (Sandbox Code Playgroud)

这将等待我输入一些内容,然后继续执行程序的其余部分。您也可以使用/dev/tty$(tty)

  • 可以说,`read var &lt;/dev/tty` 比假设 stdout 连接到控制 tty 更好。 (3认同)