Let's say I have the results of a call to Select-String in a variable $mat, parsing a regular expression from file contents:
$mat = cat errors.txt | Select-String "'(?<code>\w+)'.+ID (?<id>[^:]+)"
According to the output of $mat | Get-Member
the result contains a Matches property of type Match[]
.
When I execute the following I get all the matches of my regular expression output:
PS > $mat | Select-Object -Property Matches
Matches
-------
{'ACCFWD', ID 16}
{'EQASIAN', ID 448}
Why doesn't this next block of code using foreach to select the Matches have the same output:
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
When displaying properties, PowerShell auto-formats the properties of types that do not have a display format defined in a *.format.ps1xml
file as a table for up to 4 properties. 5 or more properties displays as a list. When you select the Matches property with Select-Object
, you are selecting a single property of the Microsoft.PowerShell.Commands.MatchInfo
object. With Foreach-Object
you are displaying all of the properties for a System.Text.RegularExpressions.Match
object.
Using Select-Object -ExpandProperty Matches
will cause the output to look the same as the Foreach
because it will output RegularExpressions.Match
objects.
If you place Get-Member
after both of your examples that produce output you will see that they output different types of objects.
Edit: Here is an explanation of the formatting that happens for each command.
cat errors.txt | Select-String "'(?<code>\w+)'.+ID (?<id>[^:]+)"
The output of Select-String
is a Microsoft.PowerShell.Commands.MatchInfo
object which has 8 properties. These properties are not displayed by default because the display format for MatchInfo
is defined in PowerShellCore.format.ps1xml
to show the result of MatchInfo
's ToString()
method.
$mat | Select-Object -Property Matches
In this case, the output of Select-Object
is a custom Selected.Microsoft.PowerShell.Commands.MatchInfo
object with the Matches
property that was copied from the MatchInfo
object. Since there is no default display format defined for the Selected.Microsoft.PowerShell.Commands.MatchInfo
type, PowerShell auto formats it as a table since it has less than 5 properties (In this case Matches
is the only property).
$mat | ForEach { $_.Matches }
In the Foreach-Object
ScriptBlock, the Matches
property of the MatchInfo
object is being output. The Matches
property is a System.Text.RegularExpressions.Match
which has 6 properties. Since there is no default display format defined for the System.Text.RegularExpressions.Match
type, the Match
objects are displayed as a list because there are more than 4 properties.
If you use -ExpandProperty instead of -Property, then the two outputs will be the same.
$mat | Select-Object -ExpandProperty Matches
That works for this particular example, but for accessing a single property, Select-Object -ExpandProperty is still not exactly the same as ForEach in all cases. I would use ForEach. Also, see this example: http://community.idera.com/powershell/powertips/b/tips/posts/use-foreach-object-instead-of-select-object-expandproperty
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With