I have a switch statement that is rather long (over 120 codes) and is a simple string match to where a certain string/code will cause a certain set of commands to execute (so no need for regex). But, as I was thinking about it, it occurred to me that I would like to pass usernames through same switch to do the same task per each user, using their username.
I attempted to do this by placing a -match
inside the condition with a named capturing group regex to grab the username, but it failed:
$code1 = 'Descriptive text describing code 1'
$code2 = 'Descriptive text describing code 2'
$userName1 = 'user_UserName1'
$userName2 = 'user_UserName2'
$Codes = $code1, $code2, $userName1, $userName2
switch ($Codes) {
$code1 {
Write-Host 'Do work required by code1'
continue
}
$code2 {
Write-Host 'Do work required by code2'
continue
}
{$_ -match '^user_(?<UserName>.*)$'} {
Write-Host ('Processing user: {0}' -f $Matches.UserName)
continue
}
default {'Other'}
}
Failed results:
Do work required by code1
Do work required by code2
Processing user:
Processing user:
Where I expected this:
Do work required by code1
Do work required by code2
Processing user: UserName1
Processing user: UserName2
And while the following does work, as you can see, I'm having to execute the -match
twice:
$Codes = 'user_UserName1', 'user_UserName2'
switch ($Codes) {
{$_ -match '^user_(?<UserName>.*)$'} {
if($_ -match '^user_(?<UserName>.*)$') {
Write-Host ('Processing user: {0}' -f $Matches.UserName)
}
continue
}
default {'Other'}
}
I'm guessing the condition is a script block executed in its own scope and $Matches
isn't available outside that scope. I can't return $userName
from within the script block since I'm sure the that is how the $true
/$false
value is received by switch.
Is there a simple method to avoid matching once for switch and then again to make $Matches
available?
Using information from here, created an extension class for extending the string type to include reEscape
method. This allows me to simply add .reEscape()
to a string to create the regex escaped version of the string. For example, 'This is a test.'.reEscape()
creates "This\ is\ a\ test\.":
class MyExtensions {
static [string]reEscape([PSObject]$source) {
return [regex]::Escape($source)
}
}
Update-TypeData -TypeName 'System.String' -MemberName 'reEscape' -MemberType CodeMethod -Value ([MyExtensions].GetMethod('reEscape')) -Force
For testing, added characters that regex will need escaped ((
, )
, and .
) to $code1
and $code2
:
$code1 = 'Descriptive text (with special characters) describing code 1.'
$code2 = 'Descriptive text (with special characters) describing code 2.'
$userName1 = 'user_UserName1'
$userName2 = 'user_UserName2'
$Codes = $code1, $code2, $userName1, $userName2
Upgraded the switch statement to include the -regex
switch, and added .reEscape()
to $code1
and $code2
:
switch -Regex ($Codes) {
$code1.reEscape() {
Write-Host 'Do work required by code1'
continue
}
$code2.reEscape() {
Write-Host 'Do work required by code2'
continue
}
'^user_(?<UserName>.*)$' {
Write-Host ('Processing user: {0}' -f $Matches.UserName)
continue
}
default { 'Other' }
}
In conclusion, I'm not excited by the idea of having to add .reEscape()
to the end of strings to make them safe for regex, but it's an acceptable solution.
I'm guessing the condition is a script block executed in its own scope and
$Matches
isn't available outside that scope.
That's correct, simply proven by:
& { $null = 'user_UserName1' -match '^user_(?<UserName>.*)$' }; $Matches
# `$Matches` isn't populated in the caller scope
Is there a simple method to avoid matching once for switch and then again to make
$Matches
available?
switch
has a -Regex
parameter and the $Matches
variable is populated when using it.
# NOTE: $var = [regex]::Escape('....')
# can be used on variable assignment for literal match on switch conditions
$code1 = 'Descriptive text describing code 1'
$code2 = 'Descriptive text describing code 2'
$userName1 = 'user_UserName1'
$userName2 = 'user_UserName2'
$Codes = $code1, $code2, $userName1, $userName2
switch -Regex ($Codes) {
$code1 {
Write-Host 'Do work required by code1'
continue
}
$code2 {
Write-Host 'Do work required by code2'
continue
}
'^user_(?<UserName>.*)$' {
Write-Host ('Processing user: {0}' -f $Matches.UserName)
continue
}
default {
'Other'
}
}