關於 switch 語句,您曾經想要知道的一切

與許多其他語言一樣,PowerShell 有命令可控制腳本內的執行流程。 其中一個語句是 switch 語句,在 PowerShell 中,它提供其他語言中找不到的功能。 今天,我們深入探討如何使用PowerShell switch

注意

本文的原始版本出現在@KevinMarquette撰寫的部落格上。 PowerShell 小組感謝 Kevin 與我們分享此內容。 請查看他在 PowerShellExplained.com部落格。

if 陳述式

您學習的第一個語句之一是 if 語句。 它可讓您在語句為 $true時執行腳本區塊。

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

您可以使用和 else 語句來擁有更複雜的邏輯elseif。 以下是一個範例,其中我有星期幾的數值,而我想以字串的形式取得名稱。

$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 語句

switch語句可讓您提供變數和可能值的清單。 如果值符合變數,則會執行其 scriptblock。

$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。 您可以使用和 foreach 語句來執行相同的動作if

預設

我們可以使用 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

如果您的陣列中重複專案,則適當區段會比對這些專案多次。

PSItem

您可以使用 $PSItem$_ 來參考處理過的目前專案。 當我們執行簡單的比對時, $PSItem 就是我們要比對的值。 我將在下一節中執行一些進階相符專案,在此區段中使用此變數。

參數

PowerShell switch 的獨特功能是它有一些參數,可變更其執行方式。

-CaseSensitive

根據預設,相符專案不會區分大小寫。 如果您需要區分大小寫,您可以使用 -CaseSensitive。 這可以與其他參數搭配使用。

-通 配 符

我們可以使用 參數啟用通配符支援 -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 語句支援 regex 比對,就像它執行通配符一樣。

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

我在另一篇文章中有更多使用 regex 的範例: 使用 regex 的許多方式。

-檔

switch 語句的一個鮮為人知的功能是,它可以使用 -File 參數來處理檔案。 您可以搭配檔案的路徑使用 -file ,而不是為檔案提供變數表達式。

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

其運作方式就像處理陣列一樣。 在這裡範例中,我將它與通配符比對結合,並使用 $PSItem。 這會處理記錄檔,並根據 regex 相符專案,將其轉換成警告和錯誤訊息。

進階詳細數據

既然您已瞭解所有這些記載的功能,我們可以在更進階處理的內容中使用這些功能。

運算式

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
    }
}

因為輸入檔中的一行可以同時包含 和 WarningError所以我們只想要執行第一行,然後繼續處理檔案。

休息時間

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 ,而不只是在開頭,我們會將其寫入為警告。 我們對 執行相同的動作 Warning。 一行可以同時有 和 Warning這兩個字Error,但我們只需要一個來處理。 這就是 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 語句巢狀時,可能會想要突破超過最內層的專案。 您可以將標籤放在 上,該標籤 switch 可以是 您 break的目標。

列舉

PowerShell 5.0 提供列舉,我們可以在參數中使用它們。

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

我們可以使用 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'
    }
}

它仍然以相同的方式執行,並在快速查看它時提供更好的視覺中斷。

Regex $matches

我們需要重新流覽 regex,以接觸一些不明顯的東西。 regex 的使用會填入 $matches 變數。 當我談論許多使用 regex 的方式時, 我確實會更多地使用$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

常數表達式

李戴利指出,我們可以使用常數 $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為 ,可確保 $isVisible 也設定為 $true$true 。 然後,評估時 $isVisible 會叫用其 scriptblock。 這是一個有點反直覺,但是巧妙的使用機制。

$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 處理,但您可以直接存取該值。 我會稱之為瘋狂。

其他模式

哈希表

我最受歡迎的文章之一是我在哈希表上 所做的文章。 的其中一個使用案例 hashtable 是查閱表格。 這是語句經常處理之常見模式 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 語句在表面上很簡單,但它提供了一些大多數人無法意識到的進階功能。 將這些功能串在一起會使此功能成為功能強大的功能。 我希望你學到了你以前沒有意識到的東西。