在 Windows 下,如何在 cmd.exe 中管道命令时取消缓冲 stdout?

在 Windows 下,如何在 cmd.exe 中管道命令时取消缓冲 stdout?

鉴于以下情况:

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这样做,这就是为什么unbufferstdbuf(修改默认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)。

相关内容