我正在尝试将命令的输出通过管道传输到shell 的内置函数,并且read
我得到了不同的行为:zsh
bash
$ 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 中运行读取的要求。