我有一个相当长的 switch 语句(超过 120 个代码),它是一个简单的字符串匹配,其中某个字符串/代码将导致执行一组特定的命令(因此不需要正则表达式)。但是,当我考虑这个问题时,我突然想到我想通过相同的 switch 传递用户名,以便使用他们的用户名为每个用户执行相同的任务。
我尝试通过-match
在条件中放置一个名为捕获组正则表达式来获取用户名,但失败了:
$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'}
}
失败结果:
Do work required by code1
Do work required by code2
Processing user:
Processing user:
我期望的是:
Do work required by code1
Do work required by code2
Processing user: UserName1
Processing user: UserName2
尽管下面的操作确实有效,但正如您所见,我必须执行-match
两次:
$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'}
}
我猜想条件是在其自身范围内执行的脚本块,$Matches
在该范围之外不可用。我无法return $userName
从脚本块内部执行该条件,因为我确信这就是switch 接收$true
/值的方式。$false
是否有一种简单的方法可以避免匹配一次 switch 然后再次使其$Matches
可用?
最终代码基于圣地亚哥的回答和评论:
[regex]::Escape(
我并不十分热衷于在每个代码描述字符串前面添加一个结尾)
,但是它可以保持文本的可读性并使变量正则表达式安全:
$code1 = [regex]::Escape('Descriptive text (with special characters) describing code 1.')
$code2 = [regex]::Escape('Descriptive text (with special characters) describing code 2.')
这部分保持不变:
$userName1 = 'user_UserName1'
$userName2 = 'user_UserName2'
$Codes = $code1, $code2, $userName1, $userName2
中的代码$Codes
需要是纯文本,因此下一行删除了正则表达式转义,switch 语句现在使用 switch -regex
,并且捕获用户代码的条件简化为包含用于捕获用户名的正则表达式的字符串。
$Codes = $Codes | & {process{[regex]::Unescape($_)}}
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'}
}
稍微改变的解决方案
这是对上述解决方案的改进,通过扩展字符串类型对象来包含属性.reEscape
和.reUnescape
。这是通过以下类完成的:
class MyExtensions {
static [string]reEscape([PSObject]$source) { return [regex]::Escape($source) }
static [string]reUnescape([PSObject]$source) { return [regex]::Unescape($source) }
static [void]AddTypeProperty([string]$typeName, [string]$MemberName) {
Update-TypeData -TypeName $typeName -MemberName $MemberName -MemberType CodeProperty -Value ([MyExtensions].GetMethod($MemberName)) -Force
}
static [void]Initialize() {
[MyExtensions]::AddTypeProperty('System.String', 'reEscape')
[MyExtensions]::AddTypeProperty('System.String', 'reUnescape')
}
}
将下面这行放在代码顶部附近,它执行将属性添加到字符串类型对象的实际工作。
[MyExtensions]::Initialize()
[regex]::Escape(
从以下行中删除并将其添加.reEscape
到末尾。
$code1 = 'Descriptive text (with special characters) describing code 1.'.reEscape
$code2 = 'Descriptive text (with special characters) describing code 2.'.reEscape
现在可以通过以下方式删除转义符:
$Codes = $Codes | & {process{$_.reUnescape}}
这是正确的,只需证明:
switch
有一个-Regex
参数,并且$Matches
在使用时会填充变量。