5

考虑 Windows 命令外壳 cmd.exe 中的管道:

C:\>feed | filter

馈送过程的标准输出似乎没有达到过滤过程的标准输入,直到馈送过程运行完成之后。

这种类型的“缓冲”可能会导致长时间运行的馈送过程的输出消息出现令人讨厌的延迟(您可能希望在早期失败时点击“ctrl-c”来中断它)。

有没有办法避免这种情况,以便一旦数据可用,来自馈送过程的标准输出就达到过滤过程的标准输入?(无缓冲)

例如,以下简化示例:

饲料.bat:

@echo off
echo something
sleep 3
echo something else

过滤器.bat:

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

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

C:\>feed | filter
_something
_something else

所需的行为是打印“_something”,然后延迟 3 秒,然后打印“_something else”。

4

1 回答 1

6

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

1) FOR /F 命令在 IN() 子句中的命令完成之前不会开始迭代任何行。这适用于所有 FOR /F 变体。IN() 子句的整个结果在迭代任何行之前被缓冲。

所以你的 filter.bat 不可能展示管道的异步特性。

2) MORE 命令不会写入部分行——它会等到收到换行符后再打印到标准输出。(除非它到达文件末尾)。

如果您想真正了解管道的异步特性,最好使用从标准输入读取每个字符并立即将其写回标准输出的程序。


这是我的 FEED.BAT 版本——它写了多行,有多个停顿。它还写了三个不换行的字符,每个字符后都有一个停顿。

@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 版本 - 它从标准输入读取一个字符并将其写入标准输出,直到到达文件末尾。

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 管道相关的特殊行为。我建议阅读为什么在管道代码块内延迟扩展失败的所有答案?对许多非直观问题有一个很好的概述。

于 2014-02-05T02:29:21.890 回答