Bri*_*ian 4 windows pipe batch-file delay buffered
考虑 Windows 命令外壳 cmd.exe 中的管道:
C:\>feed | filter
Run Code Online (Sandbox Code Playgroud)
馈送过程的标准输出似乎直到馈送过程运行完成后才能达到过滤过程的标准输入。
这种类型的“缓冲”可能会导致长时间运行的馈送过程的输出消息出现烦人的延迟(您可能希望在早期失败时按“ctrl-c”来中断它)。
有没有办法避免这种情况,以便一旦数据可用,馈送过程的标准输出就达到过滤过程的标准输入?(无缓冲)
例如,以下简化示例:
feed.bat:
@echo off
echo something
sleep 3
echo something else
Run Code Online (Sandbox Code Playgroud)
过滤器.bat:
@echo off
for /F "tokens=*" %%a in ('more') do (
echo _%%a
)
Run Code Online (Sandbox Code Playgroud)
以下命令直到 3 秒后(睡眠完成时)才显示任何内容:
C:\>feed | filter
_something
_something else
Run Code Online (Sandbox Code Playgroud)
所需的行为是打印 '_something',然后延迟 3 秒,然后打印 '_something else'。
管道在 Windows cmd.exe 中是异步的。在将信息传递到右侧之前,它们不会等待左侧完成。但是您的程序没有证明这一点,原因有两个。
1) FOR /F 命令在 IN() 子句中的命令完成之前不会开始迭代任何行。这适用于所有 FOR /F 变体。在迭代任何行之前缓冲 IN() 子句的整个结果。
所以你的 filter.bat 不可能展示管道的异步特性。
2) MORE 命令不会写入部分行 - 在打印到标准输出之前,它会等待直到收到换行符。(除非它到达文件末尾)。
如果您想真正了解管道的异步特性,最好使用一个程序,从 stdin 读取每个字符并立即将其写回 stdout。
这是我的 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
Run Code Online (Sandbox Code Playgroud)
这是我的 FILTER.JS 版本 - 它从 stdin 读取一个字符并将其写出到 stdout 直到它到达文件末尾。
while (!WScript.StdIn.AtEndOfStream) WScript.Stdout.Write(WScript.StdIn.Read(1));
Run Code Online (Sandbox Code Playgroud)
这是测试行为的命令
feed | cscript //nologo filter.js
Run Code Online (Sandbox Code Playgroud)
这是输出,<pause>在更多输出之前有暂停时插入。
something
<pause>something else
1<pause>2<pause>3<pause>
Done
Run Code Online (Sandbox Code Playgroud)
我上面的测试表明管道将立即发送它收到的任何信息(假设过滤器已准备好接收它)。
进料器和/或过滤器的设计可能会掩盖自由流动行为。您的原始测试在过滤器中存在瓶颈,因为它在继续之前等待所有输入。馈线也可以托起东西。一些程序具有缓冲输出。在缓冲区已满、缓冲区已刷新或流关闭之前,馈送器可能不会发送数据。
有许多与 Windows 管道相关的特殊行为。我建议阅读为什么在管道代码块中延迟扩展失败的所有答案?对许多非直观问题的良好概述。