Powershell regex Matches[0].Groups vs Matches.Groups key name based indexing

Viewed 480

I have just encountered this weird behavior in PowerShell and I'm wondering if there is any logical explanation for it:

After running a regex match on a string:
(Yes, I know, that this might not be the best way to do so, but the issue occurred when building a pipeline and here I only present a stripped down minimal example that still exhibits the behavior.)

    $r = "asdf" | Select-String "(?<test>\w+)"

The following two expressions print the same results for me:

    $r.Matches.Groups
    $r.Matches[0].Groups

But out of these two only the second one works:

    $r.Matches.Groups['test'] 
    $r.Matches[0].Groups['test']

The weirdest thing is that if I use numeric indexes, it works in both cases.

    $r.Matches.Groups[0] 
    $r.Matches[0].Groups[0] 

Edit: I know that in this example the capture group is not necessary at all, but I only wanted to show a simple example that illustrates a problem. Originally I'm working with multiple patterns with multiple capture groups, that I would like to access by name. I know that I could solve it by just using Matches[0], but I'm interested in an explanation.

1 Answers

This is because of a PowerShell feature called property enumeration.

Since PowerShell 4.0, whenever you reference a member that doesn't exist on a collection type, PowerShell will enumerate the collection and invoke the member on each item.

That means that this expression:

$g = $r.Matches.Groups

... is basically the same as:

$g = foreach($match in $r.Matches){
    foreach($group in $match.Groups){
        $group
    }
}

So, at this point, $g is no longer a GroupCollection- it's just an array of the values that were in any group from any match in $r.Matches.

This also explains why the [0] index expression works - regular arrays can be indexed into just fine.

Related