有没有办法更快地从大型命令中流出数据?

Is there a way to stream data out faster from a large command?

假设我正在使用 get-childitem c:\*.* -recurse 并且正在通过管道传输它。在管道处理它之前,我必须等待整个 get-childitem 命令完成。有一些例外,例如 select -first 2 会神奇地停止上一个命令。无论如何,有没有一种方法可以提高输出,使其立即写入而不是吸收大量内存?我的一个想法是...(我知道这行不通,但它传达了这个想法)

[System.IO.File]::ReadLines("$(dir c:\*.* -recurse)")

我知道这是 windows 的事情,因为 Linux 会在数据出现后立即处理数据。但我知道,这是两个不同的世界。

我最担心的是 ram 使用...

这是一个很好的例子

(1..10000000) | where {$_ -like "*543*"}

这需要我的机器大约 100 秒

哪里

(1..10000000).where({$_ -like "*543*"})

只用了25秒。

I have to wait for the whole get-childitem command to complete before the pipe handles it.

否:PowerShell 管道的重点是在对象可用时一个一个地处理对象,从而充当 memory throttle 无论输入集合的大小如何,保持内存使用不变

  • 警告:不要在通过管道发送其输出的命令周围使用 (...),因为这确实会收集该命令的输出满满的,记在第一位。

  • Cmdlets,作为PowerShell的原生命令,天生就支持这种逐一流式传输。

    • 但是,某些 cmdlet,例如 Sort-ObjectGroup-Object 必须 首先将所有输入收集到内存中[ 1],作为 概念上的必要性(例如,在比较 所有 项之前,您无法生成排序输出)。谢谢,Bacon Bits

    • 类似地,诸如 ConvertTo-Json 之类的 cmdlet 仅发出 单个 输出对象,从收集到的整个输入构造一个对象前面。

  • 类似地,外部程序的标准输出输出通过逐行,因为线路可用。

  • 您可以通过将 表达式 包含在 & { ... } 中将其转换为流式命令,但是这仅在表达式尚未在内存中构建完整的对象集合时才有用;例如,
    & { 1.. 10000000 } | ...不会给你带来任何好处,但是
    & { for ($i=0; $i -lt 10000000; ++$i) { $i } } | ... 会。

  • 最终,如果源 cmdlet/程序/表达式本身不以流式方式发出输出对象(一个接一个,因为它们正在生成),你就不走运了.

但是,确实缺少停止管道处理按需的能力- 目前只有 Select-Object -First 可以做到 - 请参阅我的 this answer
有一个长期存在的 feature request on GitHub 要求一种机制来按需停止管道。


顺便说一句:使用 PSv4+ .Where() method is indeed faster than using the Where-Object cmdlet(其内置别名是 where),但 .Where() 总是要求它所操作的集合已加载到事先完整记忆。

但是,.Where() 方法 能够通过将 'First' 作为第二个参数传递来停止处理剩余的项目,该参数在第一个参数之后停止比赛; 'First'[System.Management.Automation.WhereOperatorSelectionMode] 的实例;比较
的性能 (1..1e6).Where({$_ -eq 10})
(1..1e6).Where({$_ -eq 10}, 'First')


[1] PowerShell 使用临时文件 来缓解 Unix [=] 的内存压力28=] 效用,例如;我的猜测是这样做在 PowerShell 中并不是一个真正的选项:PowerShell 处理活动对象(而不是静态字符串)的能力会带来重大的序列化/反序列化挑战是要使用的临时文件。