Perl STDOUT重定向到管道,调用sleep()后没有输出

Perl STDOUT重定向到管道,调用sleep()后没有输出,perl,pipe,stdout,sleep,tee,Perl,Pipe,Stdout,Sleep,Tee,当将脚本STDOUT重定向到管道和使用sleep()时,我在Windows(ActivePerl和草莓)上使用Perl时遇到问题。试试这个: perl -e "for (;;) { print 'Printing line ', $i++, \"\n\"; sleep(1); }" 这正如预期的那样有效。现在将其管道连接到T形三通(或某个文件,结果相同): 根本没有输出,tee什么也没有捕获。但是,perl脚本仍在运行,只是在脚本完成之前,STDOUT上没有任何内容,然后所有输出都转储到tee

当将脚本STDOUT重定向到管道和使用sleep()时,我在Windows(ActivePerl和草莓)上使用Perl时遇到问题。试试这个:

perl -e "for (;;) { print 'Printing line ', $i++, \"\n\"; sleep(1); }"
这正如预期的那样有效。现在将其管道连接到T形三通(或某个文件,结果相同):

根本没有输出,tee什么也没有捕获。但是,perl脚本仍在运行,只是在脚本完成之前,STDOUT上没有任何内容,然后所有输出都转储到tee。除非,如果标准输出缓冲区已满,脚本可能会挂起

现在,如果移除睡眠(调用),管道将按预期工作!发生什么事了


我找到了一个解决办法;使用$|=1禁用标准输出缓冲会使管道在使用睡眠时工作,但是。。。为什么?有人能解释并提供更好的解决方案吗?

您正遭受缓冲的痛苦。添加
$|=1以解除缓冲标准输出

默认情况下,除了STDERR之外的所有文件句柄都是缓冲的,但是STDOUT在连接到终端时使用最小形式的缓冲(由换行符刷新)。通过将终端替换为管道,可以恢复正常缓冲


删除
sleep
调用不会改变任何东西,只会加快速度。填充缓冲区不是几分钟,而是几毫秒。有或没有它,输出仍然是用4k或8k块编写的(取决于您的Perl版本)。

您的
tee
程序来自哪里?你说的“可能被绞死”是什么意思?我尝试了两个不同的GNU tee二进制文件,甚至还有第三个是在C#内部开发的,行为相同。此外,仅重定向到带有“>”的文件也会产生相同的行为。如果没有睡眠,一切都很好,在脚本完成之前没有输出。是的,我们有一整天都在运行的脚本,我们回来发现它们挂起了。
睡眠
唯一的变化是脚本没有立即完成,因此它的缓冲区被刷新了。这与
tee
无关。然而,我也经历过,输出恒定的应用程序会受到缓冲的影响。在我们的例子中,操作系统保持缓冲的时间太长,等待输出的减少,以至于当它最终不得不刷新和同步时,通常会花费太长时间,缓冲区会溢出,因为输出一直很高。我们强制每4分钟左右进行一次同步
$|=1
可能无法解决此问题,您可能需要定期调用
sync
。我应该补充一点,在我们的例子中,不是STDOUT的缓冲区,而是试图缓冲过多的磁盘。Ok。但是,为什么它可以与select(unde,unde,unde,1)配合使用,而与sleep(1)配合使用呢?我确实说过,我在上面的问题中找到了特定的“修复”。这仍然不能解释为什么添加睡眠调用会使其行为有所不同。它不是添加睡眠调用,而是添加
|tee
,我确实解释了为什么这会产生不同<代码>睡眠
不是这里的一个因素;不管有没有它,同样的事情都会发生。它只是在没有睡眠的情况下发生得更快。这不是tee,虽然它确实与缓冲有关,但这并不是全部。睡眠确实在改变行为,我想知道为什么。如果使用perl的sleep(),输出将完全停止(尝试写入100行,而不是打印一行)。如果将睡眠替换为等效的选择(undef、undef、undef、1),则工作正常。如果用
sleep 1
(backticks)替换它,它也可以正常工作。请尝试一下。。否,输出一次发送4k或8k,有无睡眠。不睡觉的话会快很多。您需要等待大约500或1000秒,才能使用
sleep(1)
积累足够的输出。好的,我重新测试了,您是对的,select()也有相同的行为,它是缓冲的。但是,当调用外部/shell命令(例如,使用backtick)时,行为会发生变化。这似乎会强制缓冲区刷新,因此它似乎可以正常工作。这就解释了我所看到的问题,因为我也在测试
sleep
timeout
,这是两个外部可执行文件。谢谢你的评论。
perl -e "for (;;) { print 'Printing line ', $i++, \"\n\"; sleep(1); }" | tee