zsh:命令替换不会从其父级继承标准输入

zsh:命令替换不会从其父级继承标准输入

考虑以下命令:

seq 5 | grep $(tail -n1) <(seq 9)

zsh在1中运行时:

tail: error reading 'standard input': Input/output error

现在在 中运行相同的内容bash,它输出:

5

好的。正如注释中所解释的,命令替换$(tail -n1)继承stdin自其父级。但为什么不会发生这种情况呢zsh
这是zsh唯一的事情还是其他 shell 也这样做?它记录在哪里?


现在,如果我通过以下方式运行相同的命令zsh -c

zsh -c 'seq 5 | grep $(tail -n1) <(seq 9)'

它不会打印相同的错误消息,而是会停止tail -n1并等待用户输入,因此如果我输入

19
2
4

然后按Ctrl+ D,它会打印

4

这里发生了什么 ?


1:如果重要的话,这是与zsh 5.3.1on一起的。archlinux

答案1

你会注意到bash/ksh

echo foo | echo "$(cat)"

输出foo但是

<<< foo echo "$(cat)"

没有。

在第一种情况下,$(cat)在子进程中展开,该子进程最终将echo在其标准输入从管道重定向后执行。

在第二种情况下,$(cat)在重定向之前展开。

管道和重定向是不同的东西。管道涉及一些重定向还可以并行启动命令。这发生在每个管道组件内部重定向之前。

zsh

$ sleep 1 | ps -jfH $(ps -fH >&2)
UID        PID  PPID  C STIME TTY          TIME CMD
chazelas  2495  2494  0 20:59 pts/1    00:00:00 /bin/zsh
chazelas 31201  2495  0 21:20 pts/1    00:00:00   sleep 1
chazelas 31202  2495  0 21:20 pts/1    00:00:00   ps -fH
UID        PID  PPID  PGID   SID  C STIME TTY          TIME CMD
chazelas  2495  2494  2495  2495  0 20:59 pts/1    00:00:00 /bin/zsh
chazelas 31201  2495 31201  2495  0 21:20 pts/1    00:00:00   sleep 1
chazelas 31203  2495 31201  2495  0 21:20 pts/1    00:00:00   ps -jfH

您会注意到,这次命令替换被父 shell 扩展了。

需要记住的一件事是,在 中zsh,管道被视为更像重定向,特别是在涉及mult_ios选项(默认情况下启用)时。

当你这样做时:

echo foo > file | tr o e

foo转到filetr

在:

uname | cat < /etc/issue

cat被输入 的输出uname和 的内容/etc/issue。因此,在 中,来自/和来自管道的zsh重定向必须在同一阶段发生。最好是在扩展之后。<>

无论如何,您始终可以这样做:

echo foo | { echo "$(cat)"; }

zshbash/ksh就像你总是可以做的那样:

{ echo "$(cat)"; } <<< foo

在 和bashzsh


至于产生的原因:

tail: error reading 'standard input': Input/output error

错误。在交互式 shell 中,由于命令替换是在父进程中完成的,因此不会在终端的前台进程组中完成。

tail将在父shell的进程组中运行。如果该 shell 是会话领导者,那么它将是孤立进程组,因此当tail尝试从 tty 设备读取时,它将因 EIO 而失败。

如果zsh不是会议主持人。例如,如果您zsh从另一个 shell 启动,那么进程组将收到 SIGTTIN。主 shell 进程会忽略它,但tail最终会被挂起。

相关内容