Reputation: 85
I need to verify MAC address in RAW format using RegEx and split it into an array of 6 values by 2 characters.
When I use following pattern, I get content of last iteration of capture group only:
PS C:\Windows\System32> "708BCDBC8A0D" -match "^([0-9a-z]{2}){6}$"
True
PS C:\Windows\System32> $Matches
Name Value
---- -----
1 0D
0 708BCDBC8A0D
PS C:\Windows\System32>
With what pattern can I caputere all the groups?
I need this result:
0 = 708BCDBC8A0D
1 = 70
2 = 8B
3 = CD
4 = BC
5 = 8A
6 = 0D
Upvotes: 4
Views: 1540
Reputation: 440162
As you've observed, the automatic $Matches
variable, which reflects the result of the most recent (scalar-input[1]) regular-expression-based match
operation, only ever contains the last instance of what an embedded capture group ((...)
) captured.
Generally, -match
only ever looks for at most ONE match in the input.
-matchall
operator that would find all matches and return them as an array.Direct use of the [regex]
class (System.Text.RegularExpressions.Regex
) that underlies PowerShell's regex functionality already provides that ability, namely in the form of the ::Matches()
method, in which case capture groups aren't even needed.
# Note: Inline option (?i) makes the regex case-INsensitive
# (which PowerShell's operators are BY DEFAULT).
PS> [regex]::Matches('708BCDBC8A0D', '(?i)[0-9a-f]{2}').Value
70
8B
CD
BC
8A
0D
However, with a bit of trickery, you can also use -split
, the string splitting operator:
# Note: No inline option needed: -split - like -match and -replace -
# is case-INsensitive by default.
PS> '708BCDBC8A0D' -split '([0-9a-f]{2})' -ne ''
70
8B
CD
BC
8A
0D
If can assume that all character pairs in the input strings are hex byte values, you can simplify to:
'708BCDBC8A0D' -split '(..)' -ne ''
Note:
The regex is of necessity enclosed in (...)
, a capturing group, to explicitly instruct -split
to include what it matches in the results; since the regex normally describes the separators between the substrings of interest, its matches are normally not included.
In this case it is only the "separators" we care about, whereas the substrings between them are empty strings here, so we filter them out with -ne ''
.
[1] If the LHS of a -match
operation is an array (a collection), matching occurs against each element, and the sub-array of matching elements (rather than a single Boolean) is returned. In this case, $Matches
is not populated.
Upvotes: 0
Reputation: 4644
You can not capture multiple groups with single group definition. Avoid using RegEx when unnecessary as it takes lots of CPU. Valuable for millions of recrds.
For MACs you can use special PhysicalAddress
class:
[System.Net.NetworkInformation.PhysicalAddress]::Parse('708BCDBC8A0D')
For .Net 5 (Powershell Core I think based on it) there is TryParse
method added, but in .Net 4.5 there is no TryParse
method.
To check .Net framework powershell running use [System.Reflection.Assembly]::GetExecutingAssembly().ImageRuntimeVersion
'708BCDBC8A0D' -match "^$('([A-F0-9]{2})' * 6)$"; $Matches
'708BCDBC8A0D' -match '^([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})$'; $Matches
'@(0..5) | ForEach-Object {'708BCDBC8A0D'.Substring($_ * 2, 2)}'
@(
[String]::new('708BCDBC8A0D'[0..1]),
[String]::new('708BCDBC8A0D'[2..3]),
[String]::new('708BCDBC8A0D'[4..5]),
[String]::new('708BCDBC8A0D'[6..7]),
[String]::new('708BCDBC8A0D'[8..9]),
[String]::new('708BCDBC8A0D'[10..11])
)
Upvotes: 1