PowerShell 5.1中使用Measure-Object对自定义对象数组做聚合统计的问题
PowerShell 5.1下按分组统计TimeSpan类型属性的问题与解决方案
所有操作均在Windows PowerShell 5.1环境中执行。
背景
多轮执行的脚本运行过程中会创建文件并统计耗时,结果存储在PSCustomObject类型的数组$ResultsCreate中,数据样例如下:
> $ResultsCreate | Select-Object -First 10 | ft Run Folder File Size Create --- ------ ---- ---- ------ 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_1.bin 304087,04 00:00:00.1377073 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_2.bin 608174,08 00:00:00.0776793 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_3.bin 524288 00:00:00.0780064 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_4.bin 922746,88 00:00:00.0759425 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_5.bin 503316,48 00:00:00.0842039 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_6.bin 178257,92 00:00:00.0610860 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_7.bin 1038090,24 00:00:00.0916000 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_8.bin 660602,88 00:00:00.0782461 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_9.bin 293601,28 00:00:00.0612056 1 \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv \\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_10.bin 744488,96 00:00:00.0758247
该PSCustomObject的成员定义如下:
> $ResultsCreate | Get-Member TypeName: Selected.System.Management.Automation.PSCustomObject Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Create NoteProperty timespan Create=00:00:00.1377073 File NoteProperty string File=\\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv\file_1_1.bin Folder NoteProperty string Folder=\\opidt660-vxzv\PB-VW_NKB$\Lmneom\vxzv Run NoteProperty int Run=1 Size NoteProperty double Size=304087,04
需求是按Run字段分组,统计每轮下所有文件Create时间字段的平均值、最小值、最大值。
遇到的问题
使用Measure-Object执行统计时始终无法得到预期结果:
错误尝试1:分组后遍历统计
将$ResultsCreate按Run分组为哈希表后遍历计算平均值,最终得到的$stat哈希表仅存在键,对应值为空:
> $($ResultsCreate | Group-Object -Property Run -AsHashTable).GetEnumerator() | ForEach-Object -Begin {$stat = @{}} -Process { $stat[$_.Key] = $(Measure-Object -Property $_.Value.Duration -Average) } > $stat Name Value ---- ----- 2 1
问题原因:一是属性名拼写错误,对象的时间属性为Create而非Duration;二是PowerShell 5.1的Measure-Object不支持直接访问嵌套属性。
错误尝试2:直接统计嵌套属性
忽略分组直接调用统计命令,报错提示Property参数为空:
> $ResultsCreate | Measure-Object -Property $_.Create.TotalSeconds Measure-Object : Cannot validate argument on parameter 'Property'. The argument is null or empty. Provide an argument that is not null or empty, and th en try the command again. At line:1 char:44 + $ResultsCreate | Measure-Object -Property $_.Create.TotalSeconds + ~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [Measure-Object], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.MeasureObjectCommand
问题原因:Measure-Object的-Property参数要求传入属性名的字符串,此处传入的$_.Create.TotalSeconds中的$_在参数解析阶段没有对应的管道上下文,取值为空导致参数校验失败。
解决方案
核心思路是先通过计算属性将$_.Create.TotalSeconds扩展为独立的顶级列TotalSeconds,再进行分组统计,代码如下:
> $($ResultsCreate | select-object -Property *,@{ Name = 'TotalSeconds'; Expression = { $_.Create.TotalSeconds }} | Group-Object -Property Run -AsHashTable).GetEnumerator() | ForEach-Object -Begin {$stat = @{}} -Process { $stat[$_.Key] = $_.Value | Measure-Object -Property TotalSeconds -Average -Minimum -Maximum } > $stat Name Value ---- ----- 2 Microsoft.PowerShell.Commands.GenericMeasureInfo 1 Microsoft.PowerShell.Commands.GenericMeasureInfo
补充说明:
PowerShell 6之前的版本中,
Measure-Object对哈希表输入的支持存在缺陷,从PowerShell 6开始,Measure-Object才支持对哈希表输入执行统计操作。
内容的提问来源于stack exchange,提问作者DocZerø
相关产品推荐
相关产品推荐

