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