有没有办法更快地从大型命令中流出数据?
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-Object
和 Group-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 处理活动对象(而不是静态字符串)的能力会带来重大的序列化/反序列化挑战是要使用的临时文件。
假设我正在使用 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-Object
和Group-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 处理活动对象(而不是静态字符串)的能力会带来重大的序列化/反序列化挑战是要使用的临时文件。