ZSH: 在 while read 循环中从 stdout 输出的内容丢失

5

我遇到了一个奇怪的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

现在,当我在中间添加一个延迟时,似乎任何超过 while 循环中 scp 命令运行时间的延迟,突然第二个输出和任何后续的 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

有趣的是,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

这里发生了什么?希望有人能帮我澄清一下。谢谢!

编辑:

经过进一步的尝试,似乎这与ZSH有关,因为在普通的bash-shell中可以正常工作。


1
嗯,我无法重现这个行为。由于延迟,您能否在“trip”上看到scp已经发生?也就是说,只是scp的标准输出丢失了吗?在我的端上,我可以看到两个scp调用的输出,成功或失败都有。 - joanis
1
也许我们需要更多的细节:操作系统类型和版本、bash版本、scp版本等,这样有人就可以尝试使用与您尽可能接近的环境。 - joanis
1
我添加了一些更多的信息。我还在另一台Linux计算机上尝试了一下(基本上是在相反的方向执行这个命令),确实如预期地工作。 - Tom de Boer
1
@KamilCuk,我尝试了你的建议:它给出了相同的结果。 - Tom de Boer
1
@mihi 谢谢,我没有尝试过那个变体,我认为这是一个不错的解决方法。 - Tom de Boer
显示剩余6条评论
1个回答

0
在子shell中运行整个while循环就不会有问题。这似乎是有效的:
(echo "text.txt"; sleep 2; echo "text.txt") | (while read filepath; do scp "$filepath" "trip:~/tom/"; echo $?; done)

在 scp 源代码中进行一些挖掘后发现,非输出是由此检查返回 false 引起的:progressmeter.c#L81-L85

static int
can_output(void)
{
    return (getpgrp() == tcgetpgrp(STDOUT_FILENO));
}

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

bash和zsh之间的一个区别是zsh在主shell进程中运行while循环,而bash不会(我从这里了解到)。但目前我所能挖掘到的就只有这些了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接