Allt du någonsin velat veta om switch-instruktionen
Liksom många andra språk har PowerShell kommandon för att styra körningsflödet i dina skript. En av dessa instruktioner är switch-instruktionen och i PowerShell erbjuder den funktioner som inte finns på andra språk. Idag tar vi en djupdykning i att arbeta med PowerShell switch.
Anteckning
Den ursprungliga versionen av den här artikeln visades på bloggen skriven av @KevinMarquette. PowerShell-teamet tackar Kevin för att han har delat det här innehållet med oss. Kolla in hans blogg på PowerShellExplained.com.
Instruktionen if
En av de första instruktionerna som du lär dig är -instruktionen if . Det gör att du kan köra ett skriptblock om en -instruktion är $true.
if ( Test-Path $Path )
{
Remove-Item $Path
}
Du kan ha mycket mer komplicerad logik med hjälp elseif av och else -instruktioner. Här är ett exempel där jag har ett numeriskt värde för veckodagen och jag vill hämta namnet som en sträng.
$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
Det visar sig att detta är ett vanligt mönster och det finns många sätt att hantera detta. En av dem är med en switch.
Switch-instruktion
Med instruktionen switch kan du ange en variabel och en lista över möjliga värden. Om värdet matchar variabeln körs dess 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'
I det här exemplet matchar värdet $day för ett av de numeriska värdena och sedan tilldelas rätt namn till $result. Vi utför bara en variabeltilldelning i det här exemplet, men alla PowerShell-objekt kan köras i dessa skriptblock.
Tilldela till en variabel
Vi kan skriva det sista exemplet på ett annat sätt.
$result = switch ( $day )
{
0 { 'Sunday' }
1 { 'Monday' }
2 { 'Tuesday' }
3 { 'Wednesday' }
4 { 'Thursday' }
5 { 'Friday' }
6 { 'Saturday' }
}
Vi placerar värdet på PowerShell-pipelinen och tilldelar det till $result. Du kan göra samma sak med instruktionen if och foreach .
Standardvärde
Vi kan använda nyckelordet default för att identifiera vad som ska hända om det inte finns någon matchning.
$result = switch ( $day )
{
0 { 'Sunday' }
# ...
6 { 'Saturday' }
default { 'Unknown' }
}
Här returnerar vi värdet Unknown i standardfallet.
Strängar
Jag matchade nummer i de sista exemplen, men du kan också matcha strängar.
$item = 'Role'
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Jag bestämde mig för att inte omsluta Component,Role och Location matchar i citattecken här för att markera att de är valfria. Behandlar switch dem som en sträng i de flesta fall.
Matriser
En av de coola funktionerna i PowerShell switch är hur den hanterar matriser. Om du ger en switch matris bearbetas varje element i samlingen.
$roles = @('WEB','Database')
switch ( $roles ) {
'Database' { 'Configure SQL' }
'WEB' { 'Configure IIS' }
'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL
Om du har upprepade objekt i matrisen matchas de flera gånger av lämpligt avsnitt.
PSItem
Du kan använda $PSItem eller $_ för att referera till det aktuella objektet som bearbetades. När vi gör en enkel matchning $PSItem är det värde som vi matchar. Jag kommer att utföra några avancerade matchningar i nästa avsnitt där den här variabeln används.
Parametrar
En unik funktion i PowerShell switch är att den har ett antal växelparametrar som ändrar hur det fungerar.
-CaseSensitive
Matchningarna är inte skiftlägeskänsliga som standard. Om du behöver vara skiftlägeskänslig kan du använda -CaseSensitive. Detta kan användas i kombination med de andra växelparametrarna.
-Jokertecken
Vi kan aktivera stöd för jokertecken med växeln -wildcard . Detta använder samma logik för jokertecken som operatorn -like för att utföra varje matchning.
$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
Här bearbetar vi ett meddelande och matar sedan ut det på olika strömmar baserat på innehållet.
-Regex
Switch-instruktionen stöder regexmatchningar precis som jokertecken.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
Jag har fler exempel på att använda regex i en annan artikel jag skrev: De många sätt att använda regex.
-Fil
En lite känd funktion i switch-instruktionen är att den kan bearbeta en fil med parametern -File . Du använder -file med en sökväg till en fil i stället för att ge den ett variabeluttryck.
switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
}
'Warning*'
{
Write-Warning -Message $PSItem
}
default
{
Write-Output $PSItem
}
}
Det fungerar precis som att bearbeta en matris. I det här exemplet kombinerar jag det med jokerteckenmatchning och använder $PSItem. Detta skulle bearbeta en loggfil och konvertera den till varnings- och felmeddelanden beroende på regex-matchningar.
Avancerad information
Nu när du är medveten om alla dessa dokumenterade funktioner kan vi använda dem i samband med mer avancerad bearbetning.
Uttryck
switch Kan finnas i ett uttryck i stället för en variabel.
switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}
Vad uttrycket än utvärderas till är det värde som används för matchningen.
Flera matchningar
Du kanske redan har snappat upp detta, men en switch kan matcha flera villkor. Detta gäller särskilt när du använder -wildcard eller -regex matchar. Du kan lägga till samma villkor flera gånger och alla utlöses.
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
Alla tre av dessa påståenden utlöses. Detta visar att varje villkor är markerat (i ordning). Detta gäller för bearbetning av matriser där varje objekt kontrollerar varje villkor.
Fortsätt
Normalt är det här jag skulle presentera break uttalandet, men det är bättre att vi lär oss att använda continue först. Precis som med en foreach loop continue fortsätter du till nästa objekt i samlingen eller avslutar switch om det inte finns fler objekt. Vi kan skriva om det sista exemplet med continue-instruktioner så att endast en -instruktion körs.
switch ( 'Word' )
{
'word'
{
'lower case word match'
continue
}
'Word'
{
'mixed case word match'
continue
}
'WORD'
{
'upper case word match'
continue
}
}
lower case word match
I stället för att matcha alla tre objekten matchas den första och växeln fortsätter till nästa värde. Eftersom det inte finns några värden kvar att bearbeta avslutas växeln. I nästa exempel visas hur ett jokertecken kan matcha flera objekt.
switch -Wildcard -File $path
{
'*Error*'
{
Write-Error -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Eftersom en rad i indatafilen kan innehålla både ordet Error och Warningvill vi bara att den första ska köras och sedan fortsätta bearbeta filen.
Bryt ned
En break instruktion avslutar växeln. Det här är samma beteende som continue visas för enskilda värden. Skillnaden visas vid bearbetning av en matris. break stoppar all bearbetning i växeln och continue flyttar till nästa objekt.
$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
I det här fallet, om vi träffar några rader som börjar med Error så får vi ett fel och växeln stoppas.
Detta är vad det break uttalandet gör för oss. Om vi hittar Error i strängen och inte bara i början skriver vi den som en varning. Vi gör samma sak för Warning. Det är möjligt att en rad kan ha både ordet Error och Warning, men vi behöver bara en för att bearbeta. Det här är vad uttalandet continue gör för oss.
Bryt etiketter
-instruktionen switch stöder break/continue etiketter precis som 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
}
}
}
Personligen gillar jag inte användningen av brytetiketter men jag ville påpeka dem eftersom de är förvirrande om du aldrig har sett dem förut. När du har flera switch eller foreach -instruktioner som är kapslade kanske du vill bryta dig ur mer än det inre objektet. Du kan placera en etikett på en switch som kan vara målet för din break.
Enum
PowerShell 5.0 gav oss uppräkningar och vi kan använda dem i en växel.
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
Om du vill behålla allt som starkt skrivna uppräkningar kan du placera dem i parenteser.
switch ($item )
{
([Context]::Component)
{
'is a component'
}
([Context]::Role)
{
'is a role'
}
([Context]::Location)
{
'is a location'
}
}
Parenteserna behövs här så att växeln inte behandlar värdet [Context]::Location som en literalsträng.
ScriptBlock
Vi kan använda en scriptblock för att utföra utvärderingen för en matchning om det behövs.
$age = 37
switch ( $age )
{
{$PSItem -le 18}
{
'child'
}
{$PSItem -gt 18}
{
'adult'
}
}
'adult'
Detta ökar komplexiteten och kan göra det switch svårt att läsa. I de flesta fall där du skulle använda något liknande skulle det vara bättre att använda if och elseif instruktioner. Jag skulle överväga att använda detta om jag redan hade en stor växel på plats och jag behövde två objekt för att träffa samma utvärderingsblock.
En sak som jag tror hjälper med läsbarhet är att placera scriptblock i parenteser.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Den körs fortfarande på samma sätt och ger en bättre visuell paus när du snabbt tittar på den.
Regex $matches
Vi måste gå tillbaka till regex för att beröra något som inte är omedelbart uppenbart. Användningen av regex fyller i variabeln $matches . Jag går in i användningen av $matches mer när jag talar om De många sätt att använda regex. Här är ett snabbt exempel för att visa den i praktiken med namngivna matchningar.
$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
Du kan matcha ett $null värde som inte behöver vara standardvärdet.
$values = '', 5, $null
switch ( $values )
{
$null { "Value '$_' is `$null or an empty string" }
'' { "Value '$_' is an empty string" }
default { "Value [$_] is not an empty string or `$null" }
}
Observera att det tomma strängvärdet inte matchar $null utan $null matchar både $null och en tom sträng.
Value '' is an empty string
Value [5] is not an empty string or $null
Value '' is $null or an empty string
Value '' is an empty string
Var också försiktig med tomma returer från cmdletar. Cmdletar eller pipelines som inte har några utdata behandlas som en tom matris som inte matchar något, inklusive ärendet default .
$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
$null { '$file is $null' }
default { "`$file is type $($file.GetType().Name)" }
}
# No matches
Konstant uttryck
Lee Dailey påpekade att vi kan använda ett konstant $true uttryck för att utvärdera [bool] objekt.
Tänk dig om vi har flera booleska kontroller som måste göras.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Det här är ett rent sätt att utvärdera och vidta åtgärder för status för flera booleska fält. Det coola med det här är att du kan ha en matchning som vänder statusen för ett värde som inte har utvärderats ännu.
$isVisible = $false
$isEnabled = $true
$isAdmin = $false
switch ( $true )
{
$isEnabled
{
'Do-Action'
$isVisible = $true
}
$isVisible
{
'Show-Animation'
}
$isAdmin
{
'Enable-AdminMenu'
}
}
Do-Action
Show-Animation
Om du anger $isEnabled i det här exemplet ser du till $true att $isVisible även är inställt på $true.
$isVisible När sedan utvärderas anropas dess skriptblockering. Detta är lite kontraintuitivt men är en smart användning av mekaniken.
$switch automatisk variabel
switch När bearbetar dess värden skapar den en uppräkning och anropar den $switch. Det här är en automatisk variabel som skapats av PowerShell och du kan ändra den direkt.
$a = 1, 2, 3, 4
switch($a) {
1 { [void]$switch.MoveNext(); $switch.Current }
3 { [void]$switch.MoveNext(); $switch.Current }
}
Detta ger dig resultatet av:
2
4
Genom att flytta uppräknaren framåt bearbetas inte nästa objekt av switch men du kan komma åt det värdet direkt. Jag skulle kalla det galenskap.
Andra mönster
Hashtables
En av mina mest populära inlägg är den jag gjorde på hashtables. Ett av användningsfallen för en hashtable är att vara en uppslagstabell. Det är en alternativ metod för ett vanligt mönster som en switch instruktion ofta hanterar.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Om jag bara använder en switch som en sökning använder jag ofta en hashtable istället.
Enum
PowerShell 5.0 introducerade Enum och det är också ett alternativ i det här fallet.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
Vi kan gå hela dagen och titta på olika sätt att lösa det här problemet. Jag ville bara vara säker på att du visste att du hade alternativ.
Sista ord
Switch-instruktionen är enkel på ytan, men den erbjuder vissa avancerade funktioner som de flesta inte inser är tillgängliga. Om du kombinerar dessa funktioner blir det här en kraftfull funktion. Jag hoppas att du har lärt dig något som du inte hade insett förut.