鉴于以下情况:
C:\>perl -E " say STDOUT 111; say STDERR 222; say STDOUT 333; "
111
222
333
C:\>perl -E " say STDOUT 111; say STDERR 222; say STDOUT 333; " | cat
222
111
333
由于 stdout 被缓冲而 stderr 未被缓冲,因此在管道命令时输出的顺序不会保留。如何可靠地取消缓冲 stdout 以便在管道命令时保留顺序?
我到处寻找解决方案,我尝试过像cygwin 和 Linux 中的包unbuffer
中的脚本expect
,但这只在 Cygwin 中有效(在 Cygwin 之外,在裸 cmd.exe 的环境中,顺序仍然错误,stderr 仍然比预期的更早出现。)同样stdbuf -i0 -o0 -e0 ...
任何帮助将不胜感激。
答案1
Perl 的默认 I/O 层缓冲,并且默认不stdio
这样做,这就是为什么unbuffer
和stdbuf
(修改默认stdio
缓冲)不起作用。
Perl 提供了自己的方法来控制所使用的 I/O 层:PERLIO
环境变量。Per文档man perlrun
,应该可以set PERLIO=:unix
在运行命令之前运行,或者对于原始的基于原生 Windows 句柄的 I/O,这可能仍然是实验性的/有缺陷的set PERLIO=:win32
。两者都应该通过直接进入原始系统调用来绕过正常的缓冲行为。
假设cat
它本身是无缓冲的(我相信它使用没有缓冲的原始读取和写入,所以它应该是),但这仍然不能保证您想要的行为。Perl 可以cat
立即将数据发送到,但除非cat
设法读取数据并将其写回的速度比 Perl 移动到下一行并打印到的速度更快STDERR
,否则您仍然会STDERR
先看到输出。在本地测试中(在 Linux 上,但应该非常相似)PERLIO=:unix
,使用管道传输到cat
,我看到了以下输出:
222111
333
然后:
222
111
333
然后:
111222
333
然后(连续两次):
111
222
333
重点是,即使不考虑缓冲,管道也会因进程级并行而引入竞争条件。解决该问题的唯一方法是确保两个流都转到cat
:
perl -E "say STDOUT 111; say STDERR 222; say STDOUT 333;" 2>&1 | cat
一致输出:
111
222
333
cat
因为所有数据都在执行下一个之前立即发送print
。获得(大多数情况下)可靠排序的唯一其他方法是在打印之间休眠(允许cat
赢得竞争perl
)。