PowerShell:如何获取管道集合的计数?

New*_*tes 3 size powershell count

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

PS C:\WINDOWS\system32> $(1 | get-member).count
21
Run Code Online (Sandbox Code Playgroud)

或者我可以用这些物体做点什么。

PS C:\WINDOWS\system32> $(1 | get-member) | ForEach-object {write-host $_.name}
CompareTo
Equals
...
Run Code Online (Sandbox Code Playgroud)

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

之前曾问过类似的问题,接受的答案是在适用于集合的脚本块内使用计数器变量。问题是我已经有了那个计数器,我想要的是检查该计数器的结果是否正确。所以我不想只在脚本块内计数。我想要对我通过管道发送的集合的大小进行单独、独立的测量。我怎样才能做到这一点?

mkl*_*nt0 6

如果需要处理和计数:

在脚本块内进行自己的计数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
Run Code Online (Sandbox Code Playgroud)

请注意,由于ForEach-Object的参数绑定中的一个怪癖,实际上需要-Begin传递和脚本块才能传递多个(每个输入对象)块;如果您实际上不需要和/或,请通过- 请参阅GitHub 问题 #4513-End-Process$null-Begin-End

另请注意,该$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
Run Code Online (Sandbox Code Playgroud)

警告Measure-Object忽略$null输入集合中的值 - 请参阅GitHub 问题 #10905

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


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