123testing123
123testing123

Reputation: 77

Matching Double String, how do I only match with 1 result?

I'm able to match the string I need for regex but it is matching twice.

https://regex101.com/r/KmgGwS/7

if ( $_.PSPath -match ("(?<=\::)(.*?)(?=\\)+")) {
  $matches.Values
}

For example, the input string is something like:

'Microsoft.PowerShell.Security\Certificate::CurrentUser\Root\A43489159A520F0D93D032CCAF37E7FE20A8B419'

It expect it to get:

CurrentUser

With the current code, it gets that string twice:

CurrentUser
CurrentUser

Upvotes: 0

Views: 137

Answers (2)

mklement0
mklement0

Reputation: 439277

tl;dr

$Matches[0]

contains what your regex matched overall, $Matches[1] contains what the 1st (and only) capture group (parenthesized sub-expression, (...)) matched - in your case, both values happen to be the same.

Unless you need to explicitly enumerate the overall match as well as capture-group matches, don't use $Matches.Value.

To enumerate capture-group values only, i.e. to enumerate all matches except the overall match, use:

# Enumerate all matches except the overall one (key 0)
$Matches.GetEnumerator().Where({ $_.Key -as [int] -ne 0 }).Value 

The automatic $Matches variable, in which PowerShell reflects the results of the most recent -match operation[1], is a hashtable (System.Collections.Hashtable):

  • Entry 0 ($Matches[0]) contains what the regex matched in full.

  • All other entries, if any, contain the substrings that capture groups (parenthesized subexpressions, (...)) matched, with entry 1 representing the 1st capture group's match, 2 the 2nd, and so on.

    • If you use named capture groups (e.g. (?<foo>...), the entries use that name (e.g., $Matches['foo'] or, alternatively, $Matches.foo).

    • If you use non-capturing groups ((?:...)), they result in no entry in $Matches.

    • (Similarly, look-around assertions - (?<=...), (?<!...), (?=...), and (?!...) - do not result in entries.)


As for what you tried:

$Matches.Values outputs a collection of the values of all entries in the hashtable, meaning the overall match (entry 0) as well as any capture-group matches.

Since your regex contains a capture group that effectively captures the same as the regex as a whole, (.*?), $Matches.Values outputs a collection of values that is the equivalent of array
'CurrentUser', 'CurrentUser', which, when output to the console, yields the result shown in the question.

Note that if you regex happens not to contain any capture groups, as suggested in sln's answer, $Matches.Values may appear to return a single string, but in reality it returns an ICollection instance that just happens to have only one element.

Now, that distinction between a single-element collection and a scalar may often be irrelevant in PowerShell, but it's something to be aware of, because there are cases where it matters.


[1] Caveats:
* If the regex didn't match at all, $Matches isn't updated, so the previous value may linger.
* If the LHS of -match is an array (collection), -match acts as a filter, and $Matches isn't updated. Note that $Matches is also set in the branch handlers of a switch -Regex statement.

Upvotes: 1

user557597
user557597

Reputation:

What you're seeing is the match value and the
group 1 value. Both of which contain the same thing.

If you want to see only a single value, remove the capture group.

 (?<= :: )
 .*? 
 (?= \\ )

Or, like this (?<=::).*?(?=\\)

Upvotes: 0

Related Questions