考虑以下命令:
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.1
on一起的。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
转到file
和tr
。
在:
uname | cat < /etc/issue
cat
被输入 的输出uname
和 的内容/etc/issue
。因此,在 中,来自/和来自管道的zsh
重定向必须在同一阶段发生。最好是在扩展之后。<
>
无论如何,您始终可以这样做:
echo foo | { echo "$(cat)"; }
在zsh
和bash
/ksh
就像你总是可以做的那样:
{ echo "$(cat)"; } <<< foo
在 和bash
中zsh
。
至于产生的原因:
tail: error reading 'standard input': Input/output error
错误。在交互式 shell 中,由于命令替换是在父进程中完成的,因此不会在终端的前台进程组中完成。
tail
将在父shell的进程组中运行。如果该 shell 是会话领导者,那么它将是孤立进程组,因此当tail
尝试从 tty 设备读取时,它将因 EIO 而失败。
如果zsh
不是会议主持人。例如,如果您zsh
从另一个 shell 启动,那么进程组将收到 SIGTTIN。主 shell 进程会忽略它,但tail
最终会被挂起。