Windows cmd 中的管道.exe在该过程完成之前不会转发标准输出?



在Windows命令shell cmd.exe中考虑管道:

C:>feed | filter

喂食过程中的标准输出似乎才能达到过滤过程的标准输入,直到喂食过程运行到完成后。

这种类型的"缓冲"可能会导致长期运行的喂养过程中的输出消息中令人讨厌的延迟(您可能想点击'ctrl-c'以在早期失败时中断它)。

是否有一种方法可以避免这种情况,以便一旦数据可用,就可以在过滤过程中达到过滤过程的标准输入?(没有缓冲)

例如,以下简化示例:

feed.bat:

@echo off
echo something
sleep 3
echo something else

filter.bat:

@echo off
for /F "tokens=*" %%a in ('more') do (
    echo _%%a
)

以下命令直到3秒钟后才显示任何内容(睡眠完成时):

C:>feed | filter
_something
_something else

所需的行为是打印" _something",其次是3秒的延迟,然后打印出" _ something"。

管道在Windows cmd.exe中是异步的。他们不会等待左侧完成,然后再将信息传递给右侧。但是您的程序没有证明这两个原因。

1)for/f命令不会开始迭代任何行,直到in()子句中的命令完成为止。对于/f变体都是正确的。in()子句的整个结果在迭代任何行之前进行缓冲。

因此,您的过滤器不可能证明管道的异步性。

2)更多的命令不会编写部分行 - 它等待直到收到newline字符之前,才打印到stdout。(除非到达文件的末尾)。

如果您想真正看到管道的异步性质,最好使用一个读取stdin中每个字符的程序并立即将其写回STDOUT。


这是我的feed.bat版本 - 它写了多个暂停的多行。它还写下三个字符,而无线式feed则在每个字符之后暂停。

@echo off
echo something
timeout /nobreak 3 >nul
echo something else
timeout /nobreak 3 >nul
for /l %%N in (1 1 3) do (
  <nul set /p "=%%N"
  timeout /nobreak 3 >nul
)
echo(
echo Done

这是我的filter.js版本 - 它从stdin中读取一个字符,并将其写入stdout,直到到达文件末尾。

while (!WScript.StdIn.AtEndOfStream) WScript.Stdout.Write(WScript.StdIn.Read(1));

这是测试行为的命令

feed | cscript //nologo filter.js

这是输出,每当有更多输出之前停顿时,都会插入<pause>

something
<pause>something else
1<pause>2<pause>3<pause>
Done

上面的测试表明,管道将立即发送其收到的任何信息(假设过滤器已准备就绪)。

馈线和/或过滤器的设计可能会掩盖自由流动的行为。您的原始测试在过滤器中有一个瓶颈,因为它在继续之前等待所有输入。进纸器也有可能承担一切。一些程序具有缓冲输出。馈线可能不会在缓冲区完成或刷新缓冲区或封闭流程之前发送数据。

有许多与Windows管道相关的特殊行为。我建议阅读所有答案,以了解为什么延迟的扩展在管道的代码块中会失败?为了概述许多非直觉问题。

最新更新