这是命令:
{ ( echo "to stdout"; echo "to stderr" >&2 ) > >(sleep 1; tee stdout.txt); } 2> >(sleep 2; tee stderr.txt >&2 )
这是我所看到的:
桀骜
% { ( echo "to stdout"; echo "to stderr" >&2 ) > >(sleep 1; tee stdout.txt); } 2> >(sleep 2; tee stderr.txt >&2 )
to stdout
to stderr
%
巴什
$ { ( echo "to stdout"; echo "to stderr" >&2 ) > >(sleep 1; tee stdout.txt); } 2> >(sleep 2; tee stderr.txt >&2 )
$ to stdout
to stderr
请注意,bash 在输出完成之前写入提示$
,但 zsh 在提示用户之前会等待替换进程完成%
。
我想一个合理的答案可能是“因为 zsh 就是这样工作的”,但我想知道这是否在任何地方都有记录,以便我可以了解更多信息。作为众多 bash 用户中的 zsh 用户,我尝试了解其中的差异,以便知道当我说“嘿,运行这个命令”时会发生什么。
答案1
较短的示例在两个 shell 中的行为方式相同。父 shell 不会等待。
(sleep 1; echo done) > >(cat >file) &
通过以下修改,zsh
将要等等,但bash
不会:
{ (sleep 1; echo done) & } > >(cat >file)
zshexpn(1)
手册中的“PROCESS SUBSTITUTION”下给出了类似的示例。在那里,它提到当将异步进程的输出重定向到进程替换(在两个 shell 中也是异步的)时,shell 将等待异步进程,{ ... }
但如果没有{ ... }
.
该手册将问题描述为
还有一个额外的问题
>(process)
;当它附加到外部命令时,父 shell 不会等待进程完成,因此紧随其后的命令不能依赖结果的完成。
通过将异步命令封装在一个compound中{ ... }
,“这里的额外进程是从父shell中生成的,父shell将等待它们的完成”。我对此的解释是,我们重定向到的进程>(...)
将从 shell 中生成,而 without{ ... }
则将从我们作为后台作业启动的作业中生成(因此 shell 不会等待它)。
$ZSH_SUBSHELL
如果您在重定向到的进程替换中进行输出,则可以找到这方面的证据。对于阻塞代码,它将输出1
,而对于非阻塞代码,它将输出2
,表明输出重定向作为另一个子 shell 中的子 shell 运行(主 shell 不会等待)。
shellbash
似乎以不同的方式处理这个问题,并在这两种情况下独立于父 shell 生成所有后台作业,即使$BASH_SUBSHELL
在输出进程替换中的输出表现出与 中相同的行为zsh
。