ZSH:读取丢失时从内部标准输出输出

Tom*_*oer 5 shell zsh

我遇到了一个奇怪的 ZSH 问题。我已将脚本修剪为仍能重现该问题的最小形式。

我在这里模拟的是通过子进程监视目录的更改(实际脚本使用fswatch)。因为我正在监视,这意味着我无法事先运行命令并保存输出。一个(工作)示例如下:

(echo "text.txt"; echo "text.txt") | while read filepath; do scp "$filepath" "trip:~/tom/"; echo $?; done
text.txt                    100%  822    51.1KB/s   00:00
0
text.txt                    100%  822    99.1KB/s   00:00
0
Run Code Online (Sandbox Code Playgroud)

现在,当我在中间添加延迟时,似乎任何比scpwhile 循环内命令的运行时间长的延迟,突然第二个输出和任何后续命令都scp将不可见:

(echo "text.txt"; sleep 2; echo "text.txt") | while read filepath; do scp "$filepath" "trip:~/tom/"; echo $?; done
text.txt                    100%  822    51.1KB/s   00:00
0
0
Run Code Online (Sandbox Code Playgroud)

有趣的是,echo仍然可以正常工作,并且错误会像您所期望的那样出现。

(echo "doesnotexist.txt"; sleep 2; echo "doesnotexist.txt") | while read filepath; do scp "$filepath" "trip:~/tom/"; echo $?; done
doesnotexist.txt: No such file or directory
1
doesnotexist.txt: No such file or directory
1
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?我希望有人能帮我解决这个问题。谢谢你!

编辑:

经过一番摆弄后,这似乎与 ZSH 有关,因为它在普通的 bash-shell 中工作。

mih*_*ihi 0

在子 shell 中运行整个 while 循环不会出现问题。这似乎有效:

(echo "text.txt"; sleep 2; echo "text.txt") | (while read filepath; do scp "$filepath" "trip:~/tom/"; echo $?; done)
Run Code Online (Sandbox Code Playgroud)

对 scp 源代码的一些挖掘表明,非输出是由此检查返回 false 引起的:progressmeter.c#L81-L85

(echo "text.txt"; sleep 2; echo "text.txt") | (while read filepath; do scp "$filepath" "trip:~/tom/"; echo $?; done)
Run Code Online (Sandbox Code Playgroud)

(即 scp 进程的进程组 ID 突然与“与 stdout 关联的终端上的前台进程组”不同)。

bash 和 zsh 之间的一个区别是 zsh 在主 shell 进程中运行 while 循环,而 bash 则不然(我从这里收集到的)。但这是我目前所能挖掘的。