Perl - 无法刷新 STDOUT 或 STDERR

Posted

技术标签:

【中文标题】Perl - 无法刷新 STDOUT 或 STDERR【英文标题】:Perl - can't flush STDOUT or STDERR 【发布时间】:2013-06-24 16:31:11 【问题描述】:

来自库存 Ubuntu Precise 存储库的 Perl 5.14。尝试编写一个简单的包装器来监控从一个流复制到另一个流的进度:

use IO::Handle;
while ($bufsize = read (SOURCE, $buffer, 1048576)) 
    STDERR->printflush ("Transferred $xferred of $sendsize bytes\n");
    $xferred += $bufsize;
    print TARGET $buffer;

这没有按预期执行(每次读取 1M 缓冲区时写入一行)。我最终看到第一行(空白值为 $xferred),然后什么都没有,直到第 7 行和第 8 行所有内容都刷新(在 8MB 传输上)。我已经为此绞尽脑汁了好几个小时——我已经阅读了 perldocs,我已经阅读了经典的“遭受缓冲”文章,我已经尝试了从 select 和 $|++ 到 IO::Handle 到 binmode 的所有内容( STDERR, "::unix") 给你命名。我还尝试使用 IO::Handle (TARGET->flush) 对每一行刷新 TARGET。没有骰子。

有没有其他人遇到过这种情况?我没有任何想法了。睡一秒钟“解决”了这个问题,但显然我不想每次读取缓冲区时都睡一秒钟,这样我的进度就会在屏幕上输出!

FWIW,无论我输出到 STDERR 还是 STDOUT,问题都是一样的。

【问题讨论】:

最初分配给 STDERR 的句柄开始自动刷新,所以如果你不弄乱它,只需一个普通的print 就可以了。 @ikegami - 抱歉,我在输入时记错了。我确实看到传输的 8MB 的所有 8 行(如图所示以 1MB 块缓冲),但它们在传输结束时同时出现。问题已适当编辑。 @ikegami - 没有正常打印;我开始只是使用普通打印,这给了我缓冲行为,然后我尝试设置 $|,然后我尝试使用 IO::Handle 的 autoflush 和 flush 方法,然后我尝试 syswrite 而不是 print,然后我尝试 printflush,如上所示.没有任何改变行为,包括使用 STDOUT 而不是 STDERR - 我得到第一行输出,然后在传输过程中长时间停顿,然后在传输结束时所有八行同时刷新。 SOURCETARGET 开到哪里? 我认为我们需要查看整个 Perl 源代码。 【参考方案1】:

Perl read 调用 fread(3),而不是 read(2)

这意味着它通过libc 并且可能使用比你更大的内部缓冲区;即,它会获取所有要接收的数据,然后以 1MB 的增量快速将其扔给您。

如果这个猜想是正确的,那么解决方案可能是使用sysread,它调用read(2),而不是read

【讨论】:

你说的完全正确。问题不在于 STDERR 没有刷新,问题在于我的 INPUT 没有刷新到处理它的例程,尽管在读取调用中声明了缓冲区大小!

以上是关于Perl - 无法刷新 STDOUT 或 STDERR的主要内容,如果未能解决你的问题,请参考以下文章

perl 第四弹 标量

你啥时候需要在 Perl 中 `END close STDOUT`?

使用系统命令将stdout和stderr输出重定向到文件在perl中不起作用[重复]

从cron运行时,Perl脚本不会将STDOUT输出到文件

Perl 文件操作

perl输出重定向