假设我有一个生成对象集合的过程。对于一个非常简单的示例,请考虑使用$(1 | get-member)。我可以获得生成的对象数量:

PS C:\WINDOWS\system32> $(1 | get-member).count
21

或者我可以对那些对象做些事情。
PS C:\WINDOWS\system32> $(1 | get-member) | ForEach-object {write-host $_.name}
CompareTo
Equals
...

仅使用21个对象,执行上述操作就没有问题。但是,如果该过程生成成千上万个对象怎么办?然后,我不想只计算对象数量然后再次运行它以执行我想对它们执行的操作一次。那么,如何获取管道中发送的集合中的对象数呢?

之前曾询问过similar question,并且可接受的答案是在可用于集合的脚本块内使用计数器变量。问题是我已经有了该计数器,而我想要的是检查该计数器的结果是否正确。因此,我不想只在脚本块中计数。我想要一个单独的,独立的指标来衡量我沿着管道发送的集合的大小。我怎样才能做到这一点?

最佳答案

如果需要处理和计数:

最好在ForEach-Object脚本块内进行计数,以避免两次通过处理。



可靠地为每个输入对象(包括ForEach-Object值)调用$null,因此无需再次检查。

如果希望将处理和计数更清晰地分开,可以将多个-Process脚本块传递给ForEach-Object(在此示例中,{ $_ + 1 }是输入处理脚本块,而{ ++$count }是输入计数脚本):

PS> 1..5 | ForEach-Object -Begin { $count = 0 } `
                          -Process { $_ + 1 }, { ++$count } `
                          -End { "--- count: $count" }

2
3
4
5
6
--- count: 5

注意,由于ForEach-Object的参数绑定(bind)中有一个怪癖,实际上需要传递-Begin-End脚本块,以便传递多个-Process(每个输入对象)块。如果您实际上不需要$null和/或-Begin,则通过-End-请参阅this GitHub issue

还要注意,$count变量位于调用者的作用域内,而不是ForEach-Object调用的作用域。也就是说,$count = 0可能会更新一个预先存在的$count变量,并且,如果该变量以前不存在,则在ForEach-Object调用之后继续存在。

如果仅需要计数:

Measure-Object 是可与管道中的大型流输入集合一起使用的cmdlet [1]:

以下示例一个接一个地生成100,000个整数,并用Measure-Object一对一地对其进行计数,而没有将整个输入收集到内存中。

PS> (& { $i=0; while ($i -lt 1e5) { (++$i) } } | Measure-Object).Count
100000

警告:Measure-Object忽略输入集合中的$null值-请参阅this GitHub issue

请注意,虽然对输入对象进行计数是Measure-Object的默认行为,但它还支持多种其他操作,例如对-Sum求和和求平均值(-Average),这些操作可以组合在一个调用中。

[1] Measure-Object作为cmdlet,能够以流方式处理输入,这意味着它在接收对象时一一统计接收到的对象,这意味着即使是非常大的流输入集(也创建了一个)例如,使用Import-Csv枚举大型CSV文件的行)可以在没有内存用完的情况下进行处理-无需将输入集合整体加载到内存中。但是,如果(a)输入集合已经在内存中,或者(b)它可以装入内存并且性能很重要,则使用(...).Count

10-06 13:54
查看更多