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

PowerShell: How to get count of piped collection?

假设我有一个生成对象集合的进程。举一个非常简单的例子,考虑 $(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个对象,做上面的没有问题。但是,如果该过程生成数十万个对象怎么办?然后我不想 运行 这个过程一次只是为了计算对象然后再次 运行 它来执行我想对它们做的事情。那么我怎样才能得到一个集合中的对象计数呢?

A 之前被问过,接受的答案是在脚本块内使用一个计数器变量来处理集合。问题是我已经有了那个计数器,我想要的是检查那个计数器的结果是否正确。所以我不想只在脚本块内计数。我想要一个单独的、独立的度量方法来衡量我发送到管道中的集合的大小。我该怎么做?

如果需要处理计数:

ForEach-Object 脚本块内进行自己的计数是避免分两次处理的最佳选择。

The problem is that I already have that counter and what I want is to check that the outcome of that counter is correct.

ForEach-Object 为每个输入对象可靠地调用,包括 $null 值,因此不需要仔细检查。

如果你想要更清晰的分离处理和计数,你可以将多个 -Process脚本块传递给ForEach-Object(在本例中,{ $_ + 1 } 是 input-processing 脚本块,{ ++$count } 是 input-counting一个):

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

2
3
4
5
6
--- count: 5

请注意,由于 ForEach-Object 的参数绑定中的一个怪癖,传递 -Begin-End 脚本块实际上是 必需的 为了传递多个 -Process (每个输入对象)块;如果您实际上不需要 -Begin and/or -End,请传递 $null - 请参阅 GitHub issue #4513.

另请注意,$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 值 - 参见 GitHub issue #10905.

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


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