Powershell中的Select-Object与ForEach对Select-String结果的区别

6
假设我有一个变量 $mat,其中包含调用 Select-String 函数并从文件内容中解析正则表达式的结果:
$mat = cat errors.txt | Select-String "'(?<code>\w+)'.+ID (?<id>[^:]+)" 

根据$mat | Get-Member的输出,结果包含了一个类型为Match[]的匹配属性。
当我执行以下代码时,会输出正则表达式所有的匹配项:
PS > $mat | Select-Object -Property Matches

Matches                                                                                                                                                                              
-------                                                                                                                                                                              
{'ACCFWD', ID 16}                                                                                                                                                                    
{'EQASIAN', ID 448}                   
    

为什么使用foreach选择Matches的下一个代码块输出不同:

    PS > $mat | ForEach { $_.Matches } 


Groups   : {'ACCFWD', ID 16, ACCFWD, 16}
Success  : True
Captures : {'ACCFWD', ID 16}
Index    : 20
Length   : 15
Value    : 'ACCFWD', ID 16

Groups   : {'EQASIAN', ID 448, EQASIAN, 448}
Success  : True
Captures : {'EQASIAN', ID 448}
Index    : 20
Length   : 17
Value    : 'EQASIAN', ID 448
2个回答

11
在显示属性时,PowerShell会自动将在*.format.ps1xml文件中未定义显示格式的类型的属性格式化为表格,最多可显示4个属性。5个或更多的属性会显示为列表。当使用Select-Object选择Matches属性时,您正在选择Microsoft.PowerShell.Commands.MatchInfo对象的单个属性。使用Foreach-Object,您将显示System.Text.RegularExpressions.Match对象的所有属性。
使用Select-Object -ExpandProperty Matches将导致输出与Foreach相同,因为它将输出RegularExpressions.Match对象。如果在产生输出的两个示例之后放置Get-Member,则会看到它们输出不同类型的对象。 编辑:以下是每个命令所执行的格式说明。
cat errors.txt | Select-String "'(?<code>\w+)'.+ID (?<id>[^:]+)"
Select-String 的输出是一个 Microsoft.PowerShell.Commands.MatchInfo 对象,该对象具有8个属性。这些属性默认情况下不会显示,因为 MatchInfo 的显示格式定义在 PowerShellCore.format.ps1xml 中,以显示 MatchInfoToString() 方法的结果。
$mat | Select-Object -Property Matches
在这种情况下,Select-Object 的输出是一个定制的 Selected.Microsoft.PowerShell.Commands.MatchInfo 对象,它拥有从 MatchInfo 对象中复制来的 Matches 属性。由于没有为 Selected.Microsoft.PowerShell.Commands.MatchInfo 类型定义默认显示格式,因此 PowerShell 会自动将其格式化为表格,因为它少于 5 个属性(在这种情况下 Matches 是唯一的属性)。
$mat | ForEach { $_.Matches } 

Foreach-Object的ScriptBlock中,输出了MatchInfo对象的Matches属性。这个Matches属性是一个System.Text.RegularExpressions.Match,它有6个属性。由于System.Text.RegularExpressions.Match类型没有默认的显示格式,所以Match对象会被显示为列表,因为有超过4个属性。


谢谢您的回答,但我仍然感到困惑。ForEach的输出是有意义的;因为Matches属性是一个RegularExpression.Match对象的数组,所以我希望看到每个Match对象的所有属性。但我不明白Select-Object输出的是什么。我注意到,如果我嵌套另一个ForEach,我会得到与使用ForEach相同的等效输出:$mat | ForEach { $_.Matches | ForEach {$_.Value} }。Select-Object是否对Match对象进行了某种扩展?如果是这样,我应该如何知道这一点(在哪里记录,还会发生在哪里)? - Mikeyg36
如果您查看help Select-Object,您将看到“它会复制输入对象的这些属性的值,并创建具有指定属性和已复制值的新对象。” 如果您使用-ExpandProperty,它将输出该属性类型的对象。 - Rynant
我在原回答中忘记提到 *.format.ps1xml 文件了。我已经更新了回答,并解释了每个输出的含义。希望现在更清楚了。 - Rynant
1
没错,它是一个Match[](一个Match数组)。但是当数组通过Powershell管道发送时,它们通常会一次发送一个元素。所以管道下游的命令将接收到一个Match对象,而不是整个Match[]数组。执行$m ='test'| Select-String test。然后,$m.Matches.GetType()将结果为Match[],而$m.Matches|gm将结果为Match - Rynant
那个链接非常有帮助。在你的帮助和另一个论坛上某人的帮助下,我终于找到了答案:http://social.technet.microsoft.com/Forums/en-US/winserverpowershell/thread/5264f048-014b-42d1-a8bd-d246c602d6c0/ - Mikeyg36
显示剩余8条评论

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接