Alles wat u ooit wilde weten over de switch-instructie
Net als in veel andere talen heeft PowerShell opdrachten voor het beheren van de uitvoeringsstroom binnen uw scripts. Een van deze instructies is de switch-instructie en in PowerShell biedt het functies die niet in andere talen worden gevonden. Vandaag gaan we dieper in op het werken met PowerShell switch.
Notitie
De oorspronkelijke versie van dit artikel verscheen op de blog geschreven door @KevinMarquette. Het PowerShell-team bedankt Kevin voor het delen van deze inhoud met ons. Bekijk zijn blog op PowerShellExplained.com.
De if instructie
Een van de eerste instructies die u leert, is de if instructie. Hiermee kunt u een scriptblok uitvoeren als een instructie is $true.
if ( Test-Path $Path )
{
Remove-Item $Path
}
U kunt veel complexere logica hebben met behulp van elseif en else instructies. Hier volgt een voorbeeld waarin ik een numerieke waarde heb voor de dag van de week en ik de naam als tekenreeks wil ophalen.
$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
Het blijkt dat dit een gemeenschappelijk patroon is en dat er veel manieren zijn om dit aan te pakken. Een van hen is met een switch.
Switch-instructie
Met de switch instructie kunt u een variabele en een lijst met mogelijke waarden opgeven. Als de waarde overeenkomt met de variabele, wordt het scriptblok uitgevoerd.
$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'
In dit voorbeeld komt de waarde overeen $day met een van de numerieke waarden en wordt de juiste naam toegewezen aan $result. In dit voorbeeld wordt alleen een variabeletoewijzing uitgevoerd, maar powerShell kan in deze scriptblokken worden uitgevoerd.
Toewijzen aan een variabele
We kunnen dat laatste voorbeeld op een andere manier schrijven.
$result = switch ( $day )
{
0 { 'Sunday' }
1 { 'Monday' }
2 { 'Tuesday' }
3 { 'Wednesday' }
4 { 'Thursday' }
5 { 'Friday' }
6 { 'Saturday' }
}
We plaatsen de waarde in de PowerShell-pijplijn en wijzen deze toe aan de $result. U kunt hetzelfde doen met de if en foreach instructies.
Standaard
We kunnen het default trefwoord gebruiken om te bepalen wat er moet gebeuren als er geen overeenkomst is.
$result = switch ( $day )
{
0 { 'Sunday' }
# ...
6 { 'Saturday' }
default { 'Unknown' }
}
Hier retourneren we de waarde Unknown in de standaardcase.
Tekenreeksen
Ik was in die laatste voorbeelden overeenkomende getallen, maar u kunt ook tekenreeksen vergelijken.
$item = 'Role'
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Ik besloot de , ComponentRole en Location komt niet overeen met aanhalingstekens hier om te benadrukken dat ze optioneel zijn. In switch de meeste gevallen worden deze behandeld als een tekenreeks.
Matrices
Een van de handige functies van PowerShell switch is de manier waarop deze matrices verwerkt. Als u een switch matrix geeft, wordt elk element in die verzameling verwerkt.
$roles = @('WEB','Database')
switch ( $roles ) {
'Database' { 'Configure SQL' }
'WEB' { 'Configure IIS' }
'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL
Als u items in uw matrix hebt herhaald, worden deze meerdere keren vergeleken met de juiste sectie.
PSItem
U kunt het $PSItem huidige item gebruiken of $_ ernaar verwijzen dat is verwerkt. Wanneer we een eenvoudige overeenkomst uitvoeren, $PSItem is de waarde die we vergelijken. Ik voer enkele geavanceerde overeenkomsten uit in de volgende sectie waarin deze variabele wordt gebruikt.
Parameters
Een unieke functie van PowerShell switch is dat het een aantal switchparameters heeft die de werking ervan wijzigen.
-Hoofdlettergevoelig
De overeenkomsten zijn standaard niet hoofdlettergevoelig. Als u hoofdlettergevoelig moet zijn, kunt u dit gebruiken -CaseSensitive. Dit kan worden gebruikt in combinatie met de andere schakelparameters.
-Jokerteken
We kunnen ondersteuning voor jokertekens inschakelen met de -wildcard switch. Hierbij wordt dezelfde jokertekenlogica gebruikt als de -like operator om elke overeenkomst uit te voeren.
$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
Hier verwerken we een bericht en voeren we het vervolgens uit op verschillende streams op basis van de inhoud.
-Regex
De switch-instructie ondersteunt regex-overeenkomsten, net zoals het jokertekens doet.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
Ik heb meer voorbeelden van het gebruik van regex in een ander artikel dat ik heb geschreven: De vele manieren om regex te gebruiken.
-Bestand
Een weinig bekende functie van de switch-instructie is dat het een bestand met de -File parameter kan verwerken. U gebruikt -file een pad naar een bestand in plaats van een variabele expressie te geven.
switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
}
'Warning*'
{
Write-Warning -Message $PSItem
}
default
{
Write-Output $PSItem
}
}
Het werkt net als het verwerken van een matrix. In dit voorbeeld combineer ik het met jokertekens en maak ik gebruik van de $PSItem. Hiermee wordt een logboekbestand verwerkt en geconverteerd naar waarschuwings- en foutberichten, afhankelijk van de regex-overeenkomsten.
Geavanceerde details
Nu u zich bewust bent van al deze gedocumenteerde functies, kunnen we deze gebruiken in de context van geavanceerdere verwerking.
Expressies
De switch expressie kan zich in plaats van een variabele bevinden.
switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}
Wat de expressie ook evalueert, is de waarde die wordt gebruikt voor de overeenkomst.
Meerdere overeenkomsten
Mogelijk hebt u dit al opgepikt, maar een switch kan overeenkomen met meerdere voorwaarden. Dit geldt met name bij het gebruik -wildcard of -regex overeenkomsten. U kunt dezelfde voorwaarde meerdere keren toevoegen en ze allemaal worden geactiveerd.
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
Alle drie deze verklaringen worden ontslagen. Dit laat zien dat elke voorwaarde wordt gecontroleerd (in volgorde). Dit geldt voor het verwerken van matrices waarbij elk item elke voorwaarde controleert.
Doorgaan
Normaal gesproken zou ik de break verklaring introduceren, maar het is beter dat we eerst leren gebruiken continue . Net als bij een foreach lus gaat continue u verder naar het volgende item in de verzameling of sluit u het switch item af als er geen items meer zijn. We kunnen dat laatste voorbeeld herschrijven met continue instructies, zodat slechts één instructie wordt uitgevoerd.
switch ( 'Word' )
{
'word'
{
'lower case word match'
continue
}
'Word'
{
'mixed case word match'
continue
}
'WORD'
{
'upper case word match'
continue
}
}
lower case word match
In plaats van alle drie de items te vergelijken, wordt de eerste overeenkomend en wordt de switch voortgezet naar de volgende waarde. Omdat er geen waarden meer moeten worden verwerkt, wordt de schakeloptie afgesloten. In dit volgende voorbeeld ziet u hoe een jokerteken kan overeenkomen met meerdere items.
switch -Wildcard -File $path
{
'*Error*'
{
Write-Error -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Omdat een regel in het invoerbestand zowel het woord ErrorWarningkan bevatten als , moeten we alleen de eerste regel uitvoeren en vervolgens doorgaan met het verwerken van het bestand.
Breken
Met break een instructie wordt de schakelaar afgesloten. Dit is hetzelfde gedrag dat continue voor enkelvoudige waarden aangeeft. Het verschil wordt weergegeven bij het verwerken van een matrix. break stopt alle verwerking in de schakelaar en continue gaat naar het volgende item.
$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
In dit geval krijgen we een foutmelding als er regels worden bereikt die beginnen Error en de schakeloptie stopt.
Dit is wat die break verklaring voor ons doet. Als we de tekenreeks vinden Error en niet alleen aan het begin, schrijven we deze als waarschuwing. We doen hetzelfde voor Warning. Het is mogelijk dat een regel zowel het woord ErrorWarningals , maar we hoeven er maar één te verwerken. Dit is wat de continue verklaring voor ons doet.
Labels verbreken
De switch instructie ondersteunt break/continue labels, net als 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
}
}
}
Ik hou persoonlijk niet van het gebruik van breaklabels, maar ik wilde ze benadrukken omdat ze verwarrend zijn als je ze nog nooit eerder hebt gezien. Als u meerdere switch of foreach geneste instructies hebt, wilt u mogelijk meer dan het binnenste item uitsplitsen. U kunt een label plaatsen op een switch label dat het doel van uw break.
Enum
PowerShell 5.0 gaf ons opsommingen en we kunnen ze gebruiken in een 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
Als u alles wilt behouden als sterk getypte opsommingen, kunt u ze tussen haakjes plaatsen.
switch ($item )
{
([Context]::Component)
{
'is a component'
}
([Context]::Role)
{
'is a role'
}
([Context]::Location)
{
'is a location'
}
}
De haakjes zijn hier nodig, zodat de schakeloptie de waarde [Context]::Location niet als een letterlijke tekenreeks behandelt.
ScriptBlock
We kunnen indien nodig een scriptblock gebruiken om de evaluatie voor een overeenkomst uit te voeren.
$age = 37
switch ( $age )
{
{$PSItem -le 18}
{
'child'
}
{$PSItem -gt 18}
{
'adult'
}
}
'adult'
Dit voegt complexiteit toe en kan uw switch leesbewerkingen lastig maken. In de meeste gevallen waarin u zoiets zou gebruiken, is het beter om te gebruiken if en elseif instructies. Ik zou dit overwegen als ik al een grote switch had en ik twee items nodig had om hetzelfde evaluatieblok te bereiken.
Een ding dat ik denk te helpen bij de leesbaarheid, is door het scriptblok tussen haakjes te plaatsen.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Het wordt nog steeds op dezelfde manier uitgevoerd en geeft een betere visuele onderbreking wanneer u deze snel bekijkt.
Regex-$matches
We moeten regex opnieuw bezoeken om iets aan te raken dat niet meteen duidelijk is. Het gebruik van regex vult de $matches variabele. Ik ga meer gebruiken $matches als ik het heb over de vele manieren om regex te gebruiken. Hier volgt een snel voorbeeld om het in actie te laten zien met benoemde overeenkomsten.
$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
U kunt overeenkomen met een $null waarde die niet de standaardwaarde hoeft te zijn.
$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" }
}
U ziet dat de lege tekenreekswaarde niet overeenkomt $null , maar $null overeenkomt met zowel een $null lege tekenreeks als een lege tekenreeks.
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
Wees ook voorzichtig met lege retourneert van cmdlets. Cmdlets of pijplijnen die geen uitvoer hebben, worden behandeld als een lege matrix die niet overeenkomt met iets, inclusief de default case.
$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
$null { '$file is $null' }
default { "`$file is type $($file.GetType().Name)" }
}
# No matches
Constante expressie
Lee Dailey wees erop dat we een constante $true expressie kunnen gebruiken om items te evalueren [bool] .
Stel dat we verschillende Booleaanse controles hebben die moeten plaatsvinden.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Dit is een schone manier om de status van verschillende Booleaanse velden te evalueren en er actie op te ondernemen. Het leuke hiervan is dat u één overeenkomst kunt hebben om de status van een waarde te spiegelen die nog niet is geëvalueerd.
$isVisible = $false
$isEnabled = $true
$isAdmin = $false
switch ( $true )
{
$isEnabled
{
'Do-Action'
$isVisible = $true
}
$isVisible
{
'Show-Animation'
}
$isAdmin
{
'Enable-AdminMenu'
}
}
Do-Action
Show-Animation
Als u $isEnabled dit in dit voorbeeld instelt, zorgt u ervoor dat dat $isVisible ook is ingesteld op $true.$true
Wanneer deze $isVisible wordt geëvalueerd, wordt het scriptblock aangeroepen. Dit is een beetje contra-intuïtief, maar is een slim gebruik van de mechanica.
automatische variabele $switch
Wanneer de switch waarden worden verwerkt, wordt er een opsomming gemaakt en wordt deze $switchaanroepen. Dit is een automatische variabele die door PowerShell is gemaakt en u kunt deze rechtstreeks bewerken.
$a = 1, 2, 3, 4
switch($a) {
1 { [void]$switch.MoveNext(); $switch.Current }
3 { [void]$switch.MoveNext(); $switch.Current }
}
Dit geeft u de resultaten van:
2
4
Door de enumerator vooruit te verplaatsen, wordt het volgende item niet verwerkt door de switch waarde, maar hebt u rechtstreeks toegang tot die waarde. Ik zou het gek noemen.
Andere patronen
Hashtables
Een van mijn populairste berichten is degene die ik heb gedaan op hashtables. Een van de use cases voor een hashtable is een opzoektabel. Dat is een alternatieve benadering van een gemeenschappelijk patroon dat vaak door een switch instructie wordt aangepakt.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Als ik alleen een switch zoekactie gebruik, gebruik ik vaak een hashtable .
Enum
PowerShell 5.0 heeft in Enum dit geval ook een optie geïntroduceerd.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
We kunnen de hele dag op verschillende manieren gaan kijken om dit probleem op te lossen. Ik wilde er gewoon voor zorgen dat je wist dat je opties had.
Laatste woorden
De switch-instructie is eenvoudig op het oppervlak, maar het biedt enkele geavanceerde functies die de meeste mensen niet realiseren zijn beschikbaar. Als u deze functies samen tekent, is dit een krachtige functie. Ik hoop dat je iets hebt geleerd dat je nog niet eerder had gerealiseerd.