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.