6

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

2 Answers 2

11

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.

Sign up to request clarification or add additional context in comments.

13 Comments

Thanks for the answer, but I am still confused. The output of the ForEach makes sense; since the Matches property is an array of RegularExpressions.Match objects, I expect to see all of the properties of each Match object. I do not understand though what Select-Object is outputting. I've noticed that I get the equivalent output using ForEach if I nest another ForEach: $mat | ForEach { $_.Matches **| ForEach {$_.Value}** }. Does Select-Object do some sort of expansion of the Match objects? And if so, how am I supposed to know this (where is it documented, where else will this happen?)
If you look at help Select-Object you will see "it copies the values of those properties from the input objects and creates new objects that have the specified properties and copied values.". If you use -ExpandProperty it will output objects of the property's type.
The Matches property of the input object is of type RegularExpressions.Match[], not MatchInfo. When we say that "the value of this property is copied", since in this case the property is a RegularExpressions.Match[], what I expect as the output of Select-Object are objects with a single Match property whose value is also of type RegularExpressions.Match[]. Where does the MatchInfo all of a sudden come from? Thanks!
Select-String returns a MatchInfo object. Matches is a property of that type. Select-String creates a custom object of type Selected.<Input Type> and copies the properties that you choose from the original object. In this case it creates a Selected.Microsoft.PowerShell.Commands.MatchInfo object with a 'Matches` property. Again, if you use the -ExpandProperty switch Select-Object will output a single property as its own object.
Right, it is a Match[] (an array of Match). But when arrays are sent down the powershell pipeline they are generally sent one element at a time. So commands further down the pipeline will receive a Match object, not the entire Match[] array. Run $m = 'test'| Select-String test. Then, $m.Matches.GetType() will result in Match[], and $m.Matches|gm will result in Match.
|
2

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.