关于_Switch

简短说明

介绍如何使用开关来处理多个条件语句。

长说明

若要检查脚本或函数中的条件,可以使用 if 语句。 if 语句可以检查许多类型的条件,包括变量的值和对象的属性。

若要检查多个条件,可以使用语句 switch 。 该 switch 语句类似于一系列 if 语句,但更简单。 该 switch 语句列出了每个条件和相应的作。 如果条件匹配,则执行该作。

重要

在比较之前,该 switch 语句将所有值转换为字符串。

语法

基本 switch 语句具有以下格式:

switch (<test-expression>) {
    <result1-to-be-matched> {<action>}
    <result2-to-be-matched> {<action>}
}

语句的 switch 语法类似于以下 if 语句:

if ("$(<result1-to-be-matched>)") -eq ("$(<test-expression>)") {<action>}
if ("$(<result2-to-be-matched>)") -eq ("$(<test-expression>)") {<action>}

表达式包括返回布尔值的文本值(字符串或数字)、变量和 scriptblock。 在比较之前,该 switch 语句将所有值转换为字符串。 有关示例,请参阅本文后面的 字符串转换影响

在表达式模式下计算。<test-expression> 如果表达式返回多个值(如数组或其他可枚举类型),则 switch 语句将单独计算每个枚举值。

<result-to-be-matched>该表达式必须解析为单个值。 该值与输入值进行比较。

default 是为没有其他匹配项时使用的操作保留的。

switch 语句可以使用 $_$switch 自动变量。 自动变量包含传递给 switch 语句的表达式的值,可用于计算并在语句范围内 <result-to-be-matched> 使用。 有关详细信息,请参阅 about_Automatic_Variables

完整的 switch 语句语法如下所示:

switch [-Regex | -Wildcard | -Exact] [-CaseSensitive] (<test-expression>) {
    string | number | variable | { <value-scriptblock> }
        { <action-scriptblock> }
    default { <action-scriptblock> } # optional
}

switch [-Regex | -Wildcard | -Exact] [-CaseSensitive] -File filename {
    string | number | variable | { <value-scriptblock> }
        { <action-scriptblock> }
    default { <action-scriptblock> }  # optional
}

如果不使用参数,switch 的行为与使用 Exact 参数的行为相同。 它针对值执行不区分大小写的匹配。 如果值是一个集合,则每个元素都将按其出现的顺序进行计算。

switch 语句必须至少包含一个条件语句。

当值与任何条件不匹配时,将触发 default 子句。 它等效于 else 语句中的 if 子句。 每个 default 语句中只允许一个 switch 子句。

switch 具有以下参数:

  • Wildcard - 指示条件为通配符字符串。 如果 match 子句不是字符串,则忽略参数。 比较不区分大小写。
  • 精确 - 指示匹配子句(如果为字符串)必须完全匹配。 如果 match 子句不是字符串,则忽略此参数。 比较不区分大小写。
  • CaseSensitive - 执行区分大小写的匹配。 如果匹配子句不是字符串,则忽略此参数。
  • 文件 - 从文件(而不是 <test-expression>)获取输入。 文件一次读取一行,由 switch 语句计算。 默认情况下,比较不区分大小写。 File 参数仅支持一个文件。 如果包含多个 File 参数,则仅使用最后一个参数。 有关详细信息,请参阅 文件 参数示例
  • Regex - 执行值与条件的正则表达式匹配。 如果 match 子句不是字符串,则忽略此参数。 比较不区分大小写。 $Matches 自动变量可用于匹配语句块。

注意

指定冲突值(如 RegexRegex)时,指定的最后一个参数优先,并忽略所有冲突参数。 还允许多个参数实例。 但是,仅使用列出的最后一个参数。

例子

以下示例演示了语句的使用 switch

简单匹配示例

在以下示例中,switch 语句将测试值 3 与每个条件进行比较。 当测试值与条件匹配时,将执行该操作。

switch (3) {
    1 { "It's one."   }
    2 { "It's two."   }
    3 { "It's three." }
    4 { "It's four."  }
}
It's three.

在此示例中,该值与列表中的每个条件进行比较。 以下 switch 语句的值为 3 有两个条件,其中演示了所有条件都经过测试。

switch (3) {
    1 { "It's one."    }
    2 { "It's two."    }
    3 { "It's three."  }
    4 { "It's four."   }
    3 { "Three again." }
}
It's three.
Three again.

使用 break 和控制 continue

如果值与多个条件匹配,则执行每个条件的操作。 若要更改此行为,请使用 breakcontinue 关键字。

break 关键字停止处理并退出 switch 语句。

continue 关键字停止处理当前值,但将继续处理任何后续值。

以下示例处理一组数字,并显示它们是否奇数或偶数。 使用 continue 关键字跳过负数。 如果遇到非数字,则使用 break 关键字终止执行。

switch (1,4,-1,3,"Hello",2,1) {
    {$_ -lt 0}           { continue }
    {$_ -isnot [int32]}  { break }
    {$_ % 2}             { "$_ is Odd" }
    {-not ($_ % 2)}      { "$_ is Even" }
}
1 is Odd
4 is Even
3 is Odd

字符串转换的影响

输入值和比较值的所有值都转换为字符串进行比较。 为了避免意外的字符串转换,请使用脚本块来评估开关值。

switch ( ([datetime]'1 Jan 1970').DayOfWeek ) {
    4            { 'The integer value matches a Thursday.' }
    "4"          { 'The numeric string matches a Thursday.' }
    "Thursday"   { 'The string value matches a Thursday.' }
    { 4 -eq $_ } { 'The expression matches a Thursday.' }
}

日期对象的 DayOfWeek 属性是枚举。 虽然枚举可以与其数值或字符串值进行比较,但 switch 语句会将该值转换为枚举的字符串表示形式。

The string value matches a Thursday.
The expression matches a Thursday.

此行为与语句中的-eq比较行为if不同。

if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) {
    'The integer value matches a Thursday.'
}
The value matches a Thursday.

在此示例中,哈希表将 switch 传递给语句。 将 switch 哈希表转换为字符串。

$test = @{
    Test  = 'test'
    Test2 = 'test2'
}

$test.ToString()
System.Collections.Hashtable

请注意,哈希表的字符串表示形式与 测试 键的值不同。

switch -Exact ($test) {
    'System.Collections.Hashtable' { 'Hashtable string coercion' }
    'test'                         { 'Hashtable value' }
}
Hashtable string coercion

用于 switch 测试哈希表中的值

在此示例中,该 switch 语句正在测试哈希表中值的类型。 必须先枚举哈希表中的项,然后才能测试这些值。 为了避免字符串转换的复杂性,请使用返回布尔值的脚本块来选择要执行的作脚本块。

$var = @{A = 10; B = 'abc'}

foreach ($key in $var.Keys) {
    switch ($var[$key].GetType()) {
        { $_ -eq [int32]  }  { "$key + 10 = $($var[$key] + 10)" }
        { $_ -eq [string] }  { "$key = $($var[$key])"           }
    }
}
A + 10 = 20
B = abc

将通配符与 switch

在此示例中,没有匹配的大小写,因此没有输出。

switch ("fourteen") {
    1     { "It's one.";   break }
    2     { "It's two.";   break }
    3     { "It's three."; break }
    4     { "It's four.";  break }
    "fo*" { "That's too many."   }
}

通过添加 default 子句,可以在没有其他条件成功时执行操作。

switch ("fourteen") {
    1       { "It's one.";   break }
    2       { "It's two.";   break }
    3       { "It's three."; break }
    4       { "It's four.";  break }
    "fo*"   { "That's too many."   }
    default { "No matches"         }
}
No matches

若要使单词 fourteen 与大小写匹配,必须使用 -Wildcard-Regex 参数。

switch -Wildcard ("fourteen") {
    1     { "It's one.";   break }
    2     { "It's two.";   break }
    3     { "It's three."; break }
    4     { "It's four.";  break }
    "fo*" { "That's too many."   }
}
That's too many.

将正则表达式与 switch

以下示例使用 -Regex 参数。

$target = 'https://bing.com'
switch -Regex ($target) {
    '^ftp\://.*$'
        {
            "$_ is an ftp address"
            break
        }
    '^\w+@\w+\.com|edu|org$'
        {
            "$_ is an email address"
            break
        }
    '^(http[s]?)\://.*$'
        {
            "$_ is a web address that uses $($Matches[1])"
            break
        }
}
https://bing.com is a web address that uses https

以下示例演示如何将脚本块用作 switch 语句条件。

switch ("Test") {
    { $_ -is [string] } { "Found a string" }
    "Test"              { "This $_ executes as well" }
}
Found a string
This Test executes as well

以下示例处理包含两个日期值的数组。 <value-scriptblock> 比较每个日期的 Year 属性。 <action-scriptblock> 显示欢迎消息或 2022 年初之前的天数。

switch ((Get-Date 1-Jan-2022), (Get-Date 25-Dec-2021)) {
    { $_.Year -eq 2021 }
        {
            $days = ((Get-Date 1/1/2022) - $_).Days
            "There are $days days until 2022."
        }
    { $_.Year -eq 2022 } { 'Welcome to 2022!' }
}

使用 读取文件的内容

switch 语句与 File 参数结合使用是逐行处理大型文件的有效方法。 PowerShell 将文件的行流式传输到 switch 语句。 每行分别处理。

可以在作语句中使用 break 关键字终止处理,然后再到达文件的末尾。 switch 语句比使用 Get-Content 逐行处理大型文件更有效。

可以将 switch -File-Wildcard-Regex 相结合,实现灵活高效的逐行模式匹配。

以下示例读取 PowerShell-Docs 存储库中的 README.md。 它输出每行,直到到达以 ##开头的行。

switch -Regex -File .\README.md {
    '^##\s' { break }
    default { $_; continue }
}

<filename> 参数接受通配符表达式,但它必须仅匹配一个文件。 以下示例与上一个示例相同,只不过它在 <filename> 参数中使用通配符。 此示例有效,因为通配符模式仅匹配一个文件。

switch -Regex -File .\README.* {
    '^##\s' { break }
    default { $_; continue }
}

如果希望将其视为文本,则必须转义字符,这些字符可以解释为通配符。

$file = (New-Item -Path 'Temp:\Foo[0]' -Value Foo -Force).FullName
switch -File $file { Foo { 'Foo' } }
# No files matching '...\Temp\Foo[0]' were found.

$fileEscaped = [WildcardPattern]::Escape($file)
switch -File $fileEscaped { foo { 'Foo' } }
# Foo

另请参阅