当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 关于 switch 语句你想知道的一切

作者:精品下载站 日期:2024-12-14 03:04:14 浏览:12 分类:玩电脑

关于 switch 语句你想知道的一切


与许多其他语言一样,PowerShell 具有用于控制脚本内执行流程的命令。这些语句之一是 switch 语句,在 PowerShell 中,它提供了其他语言中没有的功能。今天,我们深入探讨 PowerShell switch 的使用。

笔记

本文的原始版本出现在@KevinMarquette 撰写的博客上。 PowerShell 团队感谢 Kevin 与我们分享这些内容。请查看他的博客:PowerShellExplained.com。

if 语句

您学习的第一个语句是 if 语句。如果语句为 $true,它允许您执行脚本块。

if ( Test-Path $Path )
{
    Remove-Item $Path
}

您可以使用 elseifelse 语句获得更复杂的逻辑。这是一个示例,其中我有一个星期几的数值,并且我想获取字符串形式的名称。

$day = 3

if ( $day -eq 0 ) { $result = 'Sunday'        }
elseif ( $day -eq 1 ) { $result = 'Monday'    }
elseif ( $day -eq 2 ) { $result = 'Tuesday'   }
elseif ( $day -eq 3 ) { $result = 'Wednesday' }
elseif ( $day -eq 4 ) { $result = 'Thursday'  }
elseif ( $day -eq 5 ) { $result = 'Friday'    }
elseif ( $day -eq 6 ) { $result = 'Saturday'  }

$result
Wednesday

事实证明,这是一种常见的模式,并且有很多方法可以解决这个问题。其中之一是带有开关

switch语句

switch 语句允许您提供变量和可能值的列表。如果值与变量匹配,则执行其脚本块。

$day = 3

switch ( $day )
{
    0 { $result = 'Sunday'    }
    1 { $result = 'Monday'    }
    2 { $result = 'Tuesday'   }
    3 { $result = 'Wednesday' }
    4 { $result = 'Thursday'  }
    5 { $result = 'Friday'    }
    6 { $result = 'Saturday'  }
}

$result
'Wednesday'

对于本示例,$day 的值与其中一个数值匹配,然后将正确的名称分配给 $result。在本示例中,我们仅进行变量赋值,但任何 PowerShell 都可以在这些脚本块中执行。

分配给变量

我们可以用另一种方式编写最后一个例子。

$result = switch ( $day )
{
    0 { 'Sunday'    }
    1 { 'Monday'    }
    2 { 'Tuesday'   }
    3 { 'Wednesday' }
    4 { 'Thursday'  }
    5 { 'Friday'    }
    6 { 'Saturday'  }
}

我们将该值放置在 PowerShell 管道上并将其分配给 $result。您可以使用 ifforeach 语句执行相同的操作。

默认

我们可以使用 default 关键字来确定如果没有匹配项会发生什么。

$result = switch ( $day )
{
    0 { 'Sunday' }
    # ...
    6 { 'Saturday' }
    default { 'Unknown' }
}

这里我们在默认情况下返回值Unknown

弦乐

我在最后的示例中匹配数字,但您也可以匹配字符串。

$item = 'Role'

switch ( $item )
{
    Component
    {
        'is a component'
    }
    Role
    {
        'is a role'
    }
    Location
    {
        'is a location'
    }
}
is a role

我决定不在此处将 ComponentRoleLocation 匹配项用引号引起来,以强调它们是可选的。在大多数情况下,switch 将它们视为字符串。

数组

PowerShell switch 的一项很酷的功能是它处理数组的方式。如果您为 switch 提供一个数组,它会处理该集合中的每个元素。

$roles = @('WEB','Database')

switch ( $roles ) {
    'Database'   { 'Configure SQL' }
    'WEB'        { 'Configure IIS' }
    'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL

如果数组中有重复的项目,那么它们会被相应的部分多次匹配。

PS项目

您可以使用 $PSItem$_ 来引用已处理的当前项目。当我们进行简单匹配时,$PSItem 就是我们要匹配的值。我将在下一节中使用此变量执行一些高级匹配。

参数

PowerShell switch 的一个独特功能是它具有许多可以更改其执行方式的开关参数。

-CaseSensitive

默认情况下,匹配不区分大小写。如果需要区分大小写,可以使用-CaseSensitive。这可以与其他开关参数结合使用。

-Wildcard

我们可以使用 -wildcard 开关启用通配符支持。这使用与 -like 运算符相同的通配符逻辑来进行每次匹配。

$Message = 'Warning, out of disk space'

switch -Wildcard ( $message )
{
    'Error*'
    {
        Write-Error -Message $Message
    }
    'Warning*'
    {
        Write-Warning -Message $Message
    }
    default
    {
        Write-Information $message
    }
}
WARNING: Warning, out of disk space

在这里,我们正在处理一条消息,然后根据内容将其输出到不同的流上。

-Regex

switch 语句支持正则表达式匹配,就像它支持通配符一样。

switch -Regex ( $message )
{
    '^Error'
    {
        Write-Error -Message $Message
    }
    '^Warning'
    {
        Write-Warning -Message $Message
    }
    default
    {
        Write-Information $message
    }
}

我在另一篇文章中提供了更多使用正则表达式的示例:使用正则表达式的多种方法。

-File

switch 语句的一个鲜为人知的功能是它可以使用 -File 参数处理文件。您可以将 -file 与文件路径一起使用,而不是为其提供变量表达式。

switch -Wildcard -File $path
{
    'Error*'
    {
        Write-Error -Message $PSItem
    }
    'Warning*'
    {
        Write-Warning -Message $PSItem
    }
    default
    {
        Write-Output $PSItem
    }
}

它的工作原理就像处理数组一样。在此示例中,我将其与通配符匹配结合起来并使用 $PSItem。这将处理日志文件并根据正则表达式匹配将其转换为警告和错误消息。

高级细节

现在您已经了解了所有这些记录的功能,我们可以在更高级的处理环境中使用它们。

表达式

switch 可以位于表达式而不是变量上。

switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}

无论表达式的计算结果是什么,都是用于匹配的值。

多场比赛

您可能已经了解了这一点,但是 switch 可以匹配多个条件。使用 -wildcard-regex 匹配时尤其如此。您可以多次添加相同的条件,并且所有条件都会被触发。

switch ( 'Word' )
{
    'word' { 'lower case word match' }
    'Word' { 'mixed case word match' }
    'WORD' { 'upper case word match' }
}
lower case word match
mixed case word match
upper case word match

这三项声明均被驳回。这表明每个条件都被检查(按顺序)。这对于处理数组来说是正确的,其中每个项目检查每个条件。

继续

通常,这是我引入 break 语句的地方,但我们最好先学习如何使用 continue 。就像 foreach 循环一样,continue 继续到集合中的下一个项目,或者如果没有更多项目,则退出 switch。我们可以使用 continue 语句重写最后一个示例,以便只执行一个语句。

switch ( 'Word' )
{
    'word'
    {
        'lower case word match'
        continue
    }
    'Word'
    {
        'mixed case word match'
        continue
    }
    'WORD'
    {
        'upper case word match'
        continue
    }
}
lower case word match

不是匹配所有三个项目,而是匹配第一个项目,然后继续切换到下一个值。由于没有剩余值可供处理,因此切换退出。下一个示例显示通配符如何匹配多个项目。

switch -Wildcard -File $path
{
    '*Error*'
    {
        Write-Error -Message $PSItem
        continue
    }
    '*Warning*'
    {
        Write-Warning -Message $PSItem
        continue
    }
    default
    {
        Write-Output $PSItem
    }
}

由于输入文件中的一行可能同时包含单词 ErrorWarning,因此我们只希望执行第一个,然后继续处理文件。

休息

break 语句退出开关。这与 continue 为单个值呈现的行为相同。处理数组时会显示出差异。 break 停止开关中的所有处理,continue 移动到下一个项目。

$Messages = @(
    'Downloading update'
    'Ran into errors downloading file'
    'Error: out of disk space'
    'Sending email'
    '...'
)

switch -Wildcard ($Messages)
{
    'Error*'
    {
        Write-Error -Message $PSItem
        break
    }
    '*Error*'
    {
        Write-Warning -Message $PSItem
        continue
    }
    '*Warning*'
    {
        Write-Warning -Message $PSItem
        continue
    }
    default
    {
        Write-Output $PSItem
    }
}
Downloading update
WARNING: Ran into errors downloading file
write-error -message $PSItem : Error: out of disk space
+ CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException

在这种情况下,如果我们点击任何以 Error 开头的行,则会收到错误并且开关停止。这就是 break 语句为我们所做的事情。如果我们在字符串中发现 Error 而不仅仅是在开头,我们会将其写为警告。我们对警告做同样的事情。一行可能同时包含单词 ErrorWarning,但我们只需要处理一个。这就是 continue 语句为我们所做的事情。

打破标签

switch 语句支持 break/continue 标签,就像 foreach 一样。

:filelist foreach($path in $logs)
{
    :logFile switch -Wildcard -File $path
    {
        'Error*'
        {
            Write-Error -Message $PSItem
            break filelist
        }
        'Warning*'
        {
            Write-Error -Message $PSItem
            break logFile
        }
        default
        {
            Write-Output $PSItem
        }
    }
}

我个人不喜欢使用中断标签,但我想指出它们,因为如果您以前从未见过它们,它们会让人感到困惑。当您有多个嵌套的 switchforeach 语句时,您可能想要突破最里面的项目。您可以在可以作为中断目标的开关上放置一个标签。

枚举

PowerShell 5.0 为我们提供了枚举,我们可以在 switch 中使用它们。

enum Context {
    Component
    Role
    Location
}

$item = [Context]::Role

switch ( $item )
{
    Component
    {
        'is a component'
    }
    Role
    {
        'is a role'
    }
    Location
    {
        'is a location'
    }
}
is a role

如果您想将所有内容保留为强类型枚举,则可以将它们放在括号中。

switch ($item )
{
    ([Context]::Component)
    {
        'is a component'
    }
    ([Context]::Role)
    {
        'is a role'
    }
    ([Context]::Location)
    {
        'is a location'
    }
}

这里需要括号,以便开关不会将值 [Context]::Location 视为文字字符串。

ScriptBlock

如果需要,我们可以使用脚本块来执行匹配评估。

$age = 37

switch ( $age )
{
    {$PSItem -le 18}
    {
        'child'
    }
    {$PSItem -gt 18}
    {
        'adult'
    }
}
'adult'

这会增加复杂性,并使您的 switch 难以阅读。在大多数情况下,如果您要使用类似的内容,最好使用 ifelseif 语句。如果我已经有一个大型开关并且我需要两个项目来达到相同的评估块,我会考虑使用它。

我认为有助于提高可读性的一件事是将脚本块放在括号中。

switch ( $age )
{
    ({$PSItem -le 18})
    {
        'child'
    }
    ({$PSItem -gt 18})
    {
        'adult'
    }
}

它仍然以相同的方式执行,并且在快速查看时提供更好的视觉休息。

正则表达式$匹配

我们需要重新审视正则表达式来触及一些不太明显的东西。使用正则表达式填充 $matches 变量。当我谈论使用正则表达式的多种方法时,我确实更多地讨论了 $matches 的使用。这是一个快速示例,展示了它与命名匹配的实际操作。

$message = 'my ssn is 123-23-3456 and credit card: 1234-5678-1234-5678'

switch -regex ($message)
{
    '(?<SSN>\d\d\d-\d\d-\d\d\d\d)'
    {
        Write-Warning "message contains a SSN: $($matches.SSN)"
    }
    '(?<CC>\d\d\d\d-\d\d\d\d-\d\d\d\d-\d\d\d\d)'
    {
        Write-Warning "message contains a credit card number: $($matches.CC)"
    }
    '(?<Phone>\d\d\d-\d\d\d-\d\d\d\d)'
    {
        Write-Warning "message contains a phone number: $($matches.Phone)"
    }
}
WARNING: message may contain a SSN: 123-23-3456
WARNING: message may contain a credit card number: 1234-5678-1234-5678

$null

您可以匹配一个不必是默认值的 $null 值。

$values = '', 5, $null
switch ( $values )
{
    $null          { "Value '$_' is `$null" }
    { '' -eq $_ }  { "Value '$_' is an empty string" }
    default        { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null

switch 语句中测试空字符串时,使用本示例中所示的比较语句而不是原始值 '' 非常重要。在 switch 语句中,原始值 '' 也匹配 $null。例如:

$values = '', 5, $null
switch ( $values )
{
    $null          { "Value '$_' is `$null" }
    ''             { "Value '$_' is an empty string" }
    default        { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null
Value '' is an empty string

另外,请小心 cmdlet 的空返回。没有输出的 Cmdlet 或管道将被视为与任何内容都不匹配的空数组,包括 default 情况。

$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
    $null   { '$file is $null' }
    default { "`$file is type $($file.GetType().Name)" }
}
# No matches

常数表达式

Lee Dailey 指出,我们可以使用常量 $true 表达式来计算 [bool] 项。想象一下,如果我们需要进行多次布尔检查。

$isVisible = $false
$isEnabled = $true
$isSecure = $true

switch ( $true )
{
    $isEnabled
    {
        'Do-Action'
    }
    $isVisible
    {
        'Show-Animation'
    }
    $isSecure
    {
        'Enable-AdminMenu'
    }
}
Do-Action
Enabled-AdminMenu

这是一种评估多个布尔字段的状态并对其采取行动的简洁方法。这样做的一个很酷的事情是,您可以通过一次匹配来翻转尚未评估的值的状态。

$isVisible = $false
$isEnabled = $true
$isAdmin = $false

switch ( $true )
{
    $isEnabled
    {
        'Do-Action'
        $isVisible = $true
    }
    $isVisible
    {
        'Show-Animation'
    }
    $isAdmin
    {
        'Enable-AdminMenu'
    }
}
Do-Action
Show-Animation

在此示例中将 $isEnabled 设置为 $true 可确保 $isVisible 也设置为 $true。然后,当 $isVisible 被求值时,它的脚本块被调用。这有点违反直觉,但却是对机制的巧妙利用。

$switch 自动变量

switch 处理其值时,它会创建一个枚举器并调用它 $switch。这是由 PowerShell 创建的自动变量,您可以直接操作它。

$a = 1, 2, 3, 4

switch($a) {
    1 { [void]$switch.MoveNext(); $switch.Current }
    3 { [void]$switch.MoveNext(); $switch.Current }
}

这将为您提供以下结果:

2
4

通过向前移动枚举器,switch 不会处理下一项,但您可以直接访问该值。我会称之为疯狂。

其他图案

哈希表

我最受欢迎的帖子之一是我在哈希表上发表的帖子。哈希表的用例之一是用作查找表。这是 switch 语句经常处理的常见模式的另一种方法。

$day = 3

$lookup = @{
    0 = 'Sunday'
    1 = 'Monday'
    2 = 'Tuesday'
    3 = 'Wednesday'
    4 = 'Thursday'
    5 = 'Friday'
    6 = 'Saturday'
}

$lookup[$day]
Wednesday

如果我仅使用 switch 作为查找,我通常会使用 hashtable 来代替。

枚举

PowerShell 5.0 引入了 Enum,在这种情况下它也是一个选项。

$day = 3

enum DayOfTheWeek {
    Sunday
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
}

[DayOfTheWeek]$day
Wednesday

我们可以花一整天的时间寻找解决这个问题的不同方法。我只是想确保你知道你有选择。

最后的话

switch 语句表面上很简单,但它提供了一些大多数人没有意识到的高级功能。将这些功能结合在一起使其成为一个强大的功能。我希望你学到了一些你以前没有意识到的东西。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯