从 stdin 读取在 bash 和 zsh 中的工作方式不同

从 stdin 读取在 bash 和 zsh 中的工作方式不同

我正在尝试将命令的输出通过管道传输到shell 的内置函数,并且read我得到了不同的行为:zshbash

$ bash -c 'echo hello | read test; echo $test'

$ zsh -c 'echo hello | read test; echo $test'
hello

尽管这在 中不起作用bash,但以下方法对两者都适用:

 $ bash -c 'echo hello | while read test; do echo $test; done'
 hello
 $ zsh -c 'echo hello | while read test; do echo $test; done'
 hello

这是为什么?难道是我用read错了?我发现在脚本中使用它比test="$(echo hello)"迫使我更仔细地处理引用问题更具可读性。

答案1

您正在观察未使用 POSIX 标准化的结果。

POSIX 没有标准化解释器运行管道的方式。

对于历史悠久的 Bourne Shell,管道中最右边的程序甚至不是主 Shell 的子程序。这样做的原因是因为这种实现速度很慢,但需要很少的代码 - 如果您只有 64 kB 内存,这一点很重要。由于在此变体中,read命令在子进程中运行,因此在子进程中分配 shell 变量在主 shell 中不可见。

ksh像或(最近的 Bourne Shell)这样的现代 shellbosh以管道中的所有进程都是主 shell 的直接子进程的方式创建管道,如果最右边的程序是 shell 内置程序,它甚至由主 shell 运行。

这一切都是让read程序修改主 shell 的 shell 变量所必需的。因此,只有在这个变体中(顺便说一句,这是最快的变体)才允许主 shell 看到变量赋值的结果。

在第二个示例中,整个while循环在同一子进程中运行,因此允许打印 shell 变量的修改版本。

当前有一个请求添加对 POSIX shell 的支持,以获取有关任何管道命令是否具有非零退出代码的信息。为了实现这一点,必须以管道中的所有程序都是主 shell 的直接子程序的方式来实现 shell。这接近于允许的情况

echo foo | read val; echo $val

执行预期的操作,从那时起,只缺少在主 shell 中运行读取的要求。

相关内容