與許多其他語言一樣,PowerShell 有命令可控制腳本內的執行流程。 其中一個語句是 switch 語句,並在 PowerShell 中提供其他語言找不到的功能。 今天,我們會深入探討使用PowerShell switch
。
備註
本文的 原始版本 出現在 @KevinMarquette撰寫的部落格上。 PowerShell 小組感謝 Kevin 與我們分享此內容。 請在 PowerShellExplained.com查看他的部落格。
if
陳述
您學習的第一個語句之一是 if
語句。 如果某語句為 $true
,則可讓您執行腳本區塊。
if ( Test-Path $Path )
{
Remove-Item $Path
}
您可以使用 elseif
和 else
語句來擁有更複雜的邏輯。 以下是一個範例,其中我有星期幾的數值,而我想以字串的形式取得名稱。
$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
。 您可以使用 if
和 foreach
語句來執行相同的動作。
預設
我們可以使用 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
我決定不在這裡給 Component
、Role
和 Location
匹配項加上引號,以強調它們是可選的。 在大部分情況下,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 語句支援正則表達式比對,就像執行通配符一樣。
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
。 這會處理記錄檔,並根據正則表達式的匹配結果,將其轉換成警告和錯誤訊息。
進階詳細數據
既然您已瞭解所有這些已知功能,我們可以在更進階處理的內容中使用它們。
表達式
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 結束。 這個範例示範通配符比對多個項目的方法。
switch -Wildcard -File $path
{
'*Error*'
{
Write-Error -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
因為輸入檔中的一行可以同時包含字 Error
和 Warning
,所以我們只想要執行第一行,然後繼續處理檔案。
休息
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
執行相同的動作。 在一行中可能同時出現字 Error
和 Warning
,但我們只需要處理其中一個。 這就是 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
}
}
}
我個人不喜歡使用「中斷標籤」,但我想指出它們,因為如果你以前從未見過,它們可能會讓人感到困惑。 當您有多個巢狀 switch
或 foreach
語句時,您可能會想要跳出不僅是最內層的語句。 您可以將標籤放在 switch
上,而該標籤可以是 break
的目標。
列舉
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
我們可以使用「scriptblock」,以便在需要時執行匹配的評估。
$age = 37
switch ( $age )
{
{$PSItem -le 18}
{
'child'
}
{$PSItem -gt 18}
{
'adult'
}
}
'adult'
這會增加複雜性,並可讓您的 switch
難以閱讀。 在大部分情況下,如果您會使用類似這樣的東西,最好是改用 if
和 elseif
語句。 如果我已經完成了大型開關的設置,並且需要兩個項目觸發相同的評估區塊,那麼我會考慮使用這個。
我認為有助於辨別的一件事是將腳本區塊放在括弧中。
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
常數表達式
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
被評估時,其 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 語句在表面上很簡單,但它提供了一些大多數人無法意識到的進階功能。 將這些功能串在一起會使此功能成為功能強大的功能。 我希望你學到了你以前沒有意識到的東西。