Tout ce que vous vouliez savoir sur l’instruction switch

Comme de nombreux autres langages, PowerShell a des commandes pour contrôler le flux d’exécution au sein de vos scripts. L’une de ces instructions est l’instruction switch et dans PowerShell, elle offre des fonctionnalités qui ne sont pas trouvées dans d’autres langages. Aujourd’hui, nous allons approfondir l’utilisation de PowerShell switch.

Note

La version originale de cet article est apparue sur le blog écrit par @KevinMarquette. L’équipe PowerShell remercie Kevin de partager ce contenu avec nous. Veuillez consulter son blog à PowerShellExplained.com.

L’instruction if.

L’une des premières instructions que vous apprenez est l’instruction if . Il vous permet d’exécuter un bloc de script si une instruction est $true.

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

Vous pouvez avoir une logique beaucoup plus complexe à l’aide de elseif et else dans les instructions. Voici un exemple où j’ai une valeur numérique pour le jour de la semaine et je veux obtenir le nom sous forme de chaîne.

$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

Il s’avère qu’il s’agit d’un modèle commun et qu’il existe de nombreuses façons de traiter ce problème. L’un d’eux est avec un switch.

Instruction `switch`

L’instruction switch vous permet de fournir une variable et une liste de valeurs possibles. Si la valeur correspond à la variable, son bloc d’instruction est exécuté.

$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'

Pour cet exemple, la valeur de $day correspond à l’une des valeurs numériques, alors le nom correct est attribué à $result. Nous effectuons uniquement une attribution de variable dans cet exemple, mais n’importe quel PowerShell peut être exécuté dans ces blocs de script.

Affecter à une variable

Nous pouvons écrire ce dernier exemple d’une autre manière.

$result = switch ( $day )
{
    0 { 'Sunday'    }
    1 { 'Monday'    }
    2 { 'Tuesday'   }
    3 { 'Wednesday' }
    4 { 'Thursday'  }
    5 { 'Friday'    }
    6 { 'Saturday'  }
}

Nous plaçons la valeur sur le pipeline PowerShell et l'assignons au $result. Vous pouvez faire la même chose avec les instructions if et foreach.

Par défaut

Nous pouvons utiliser le default mot clé pour identifier ce qui doit se produire s’il n’y a aucune correspondance.

$result = switch ( $day )
{
    0 { 'Sunday' }
    # ...
    6 { 'Saturday' }
    default { 'Unknown' }
}

Ici, nous renvoyons la valeur Unknown dans le cas par défaut.

Chaînes

J’ai fait correspondre des nombres dans ces derniers exemples, mais vous pouvez également faire correspondre des chaînes.

$item = 'Role'

switch ( $item )
{
    Component
    {
        'is a component'
    }
    Role
    {
        'is a role'
    }
    Location
    {
        'is a location'
    }
}
is a role

J'ai décidé de ne pas mettre les correspondances Component, Role et Location entre guillemets ici pour souligner qu'elles sont facultatives. Les switch les traite comme une chaîne dans la plupart des cas.

Tableaux

L’une des fonctionnalités cool de PowerShell switch est la façon dont elle gère les tableaux. Si vous donnez un switch tableau, il traite chaque élément de cette collection.

$roles = @('WEB','Database')

switch ( $roles ) {
    'Database'   { 'Configure SQL' }
    'WEB'        { 'Configure IIS' }
    'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL

Si vous avez répété des éléments dans votre tableau, ils sont mis en correspondance plusieurs fois par la section appropriée.

PSItem

Vous pouvez utiliser $PSItem ou $_ pour référencer l’élément actuel qui a été traité. Lorsque nous faisons une correspondance simple, $PSItem est la valeur que nous mettons en correspondance. Je vais effectuer des correspondances avancées dans la section suivante où cette variable est utilisée.

Paramètres

Une fonctionnalité unique de PowerShell switch est qu’il a un certain nombre de [switch] paramètres qui changent la façon dont il effectue.

Sensible à la casse

Les correspondances ne respectent pas la casse par défaut. Si vous devez respecter la casse, vous pouvez utiliser -CaseSensitive. Cela peut être utilisé en combinaison avec les autres [switch] paramètres.

-Joker

Nous pouvons activer la prise en charge des caractères génériques avec le -Wildcard[switch] paramètre. Cela utilise la même logique générique que l’opérateur -like pour effectuer chaque correspondance.

$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

Ici, nous traitons un message, puis nous le mettons sur différents flux en fonction du contenu.

-Regex

L’instruction switch prend en charge les correspondances régulières tout comme pour les caractères génériques.

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

J’ai plus d’exemples d’utilisation de regex dans un autre article que j’ai écrit : Les nombreuses façons d’utiliser regex.

-Fichier

Une fonctionnalité peu connue de l’instruction switch est qu’elle peut traiter un fichier avec le -File paramètre. Vous utilisez -File un chemin d’accès à un fichier au lieu de lui donner une expression de variable.

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

Cela fonctionne comme on traite un tableau. Dans cet exemple, je la combine avec la correspondance de caractères génériques et utilise le $PSItem. Cela traite un fichier journal et le convertit en messages d’avertissement et d’erreur en fonction des correspondances regex.

Détails avancés

Maintenant que vous connaissez toutes ces fonctionnalités documentées, nous pouvons les utiliser dans le contexte d’un traitement plus avancé.

Expressions

Il switch peut s’agir d’une expression au lieu d’une variable.

switch ( ( Get-Service | where Status -EQ 'running' ).Name ) {...}

Quelle que soit la valeur résultant de l’évaluation de l’expression, c’est celle utilisée pour la correspondance.

Correspondances multiples

Vous l'avez peut-être déjà remarqué, mais un switch peut correspondre à plusieurs conditions. Cela est particulièrement vrai lors de l’utilisation de la correspondance -Wildcard ou -Regex. Vous pouvez ajouter la même condition plusieurs fois et tous sont déclenchés.

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

Ces trois déclarations sont déclenchées. Cela montre que chaque condition est vérifiée (dans l’ordre). Cela s'applique au traitement des tableaux où chaque élément vérifie chaque condition.

Continuer

Normalement, c’est là que je présenterais l’énoncé break , mais c’est mieux que nous apprenons à utiliser continue en premier. Tout comme avec une foreach boucle, continue continue sur l'élément suivant de la collection ou quitte le switch s'il n'y a plus d'éléments. Nous pouvons réécrire ce dernier exemple avec des instructions continue afin qu’une seule instruction s’exécute.

switch ( 'Word' )
{
    'word'
    {
        'lower case word match'
        continue
    }
    'Word'
    {
        'mixed case word match'
        continue
    }
    'WORD'
    {
        'upper case word match'
        continue
    }
}
lower case word match

Au lieu de mettre en correspondance les trois éléments, le premier est mis en correspondance et le commutateur passe à la valeur suivante. Étant donné qu’il n’y a aucune valeur à traiter, le commutateur se ferme. Cet exemple suivant montre comment un caractère générique peut correspondre à plusieurs éléments.

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

Étant donné qu’une ligne dans le fichier d’entrée peut contenir à la fois le mot Error et Warning, nous voulons que le premier soit exécuté, puis continuer à traiter le fichier.

Pause

Une break instruction quitte le commutateur. Il s’agit du même comportement que celui présenté continue pour les valeurs uniques. La différence s’affiche lors du traitement d’un tableau. break arrête tout traitement dans le commutateur et continue passe à l’élément suivant.

$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

Dans ce cas, si nous rencontrons des lignes qui commencent par Error, alors nous obtenons une erreur et le commutateur s’arrête. C’est ce que fait cette break déclaration pour nous. Si nous trouvons Error à l’intérieur de la chaîne et pas seulement au début, nous l’écrivons en tant qu’avertissement. Nous faisons la même chose pour Warning. Il est possible qu’une ligne ait à la fois le mot Error et Warning, mais nous n’avons besoin que d’un seul processus. Voici ce que l'instruction continue fait pour nous.

Étiquettes de rupture

L’instruction switch prend en charge les étiquettes break/continue comme le fait 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
        }
    }
}

Personnellement, je n’aime pas l’utilisation des étiquettes de rupture, mais je voulais les signaler parce qu’ils sont déroutants si vous ne les avez jamais vus. Lorsque vous avez plusieurs switch instructions foreach imbriquées, vous souhaiterez peut-être sortir de plus que l’élément le plus interne. Vous pouvez placer un label sur une switch qui peut être la cible de votre break.

Enum

PowerShell 5.0 nous a donné des énumérations et nous pouvons les utiliser dans un commutateur.

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

Si vous souhaitez conserver l'ensemble sous forme d'énumérations fortement typées, vous pouvez les mettre entre parenthèses.

switch ($item )
{
    ([Context]::Component)
    {
        'is a component'
    }
    ([Context]::Role)
    {
        'is a role'
    }
    ([Context]::Location)
    {
        'is a location'
    }
}

Les parenthèses sont nécessaires ici pour que le commutateur ne traite pas la valeur [Context]::Location comme une chaîne littérale.

ScriptBlock

Nous pouvons utiliser un "scriptblock" pour effectuer l’évaluation d’une correspondance si nécessaire.

$age = 37

switch ( $age )
{
    {$PSItem -le 18}
    {
        'child'
    }
    {$PSItem -gt 18}
    {
        'adult'
    }
}
'adult'

Cela ajoute de la complexité et peut rendre votre switch lecture difficile. Dans la plupart des cas où vous utiliseriez quelque chose comme cela, il serait préférable d'utiliser les instructions if et elseif. J’envisagerais d’utiliser cela si j’avais déjà un grand commutateur en place et j’avais besoin de deux éléments pour atteindre le même bloc d’évaluation.

Une chose que je pense aider à la lisibilité est de placer le scriptblock entre parenthèses.

switch ( $age )
{
    ({$PSItem -le 18})
    {
        'child'
    }
    ({$PSItem -gt 18})
    {
        'adult'
    }
}

Il s’exécute toujours de la même façon et donne un meilleur aperçu visuel lorsqu'on le regarde rapidement.

Regex $Matches

Nous devons revoir regex pour toucher à quelque chose qui n’est pas immédiatement évident. L’utilisation de regex remplit la $Matches variable. Je m'immerge davantage dans l’utilisation de $Matches quand je parle des nombreuses façons d’utiliser les expressions régulières. Voici un exemple rapide pour l’afficher en action avec des correspondances nommées.

$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

Vous pouvez correspondre à une $null valeur qui n’a pas besoin d’être la valeur par défaut.

$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

Lorsque vous testez une chaîne vide dans une switch instruction, il est important d’utiliser l’instruction de comparaison comme indiqué dans cet exemple au lieu de la valeur ''brute. Dans une switch instruction, la valeur brute '' correspond également $null. Par exemple:

$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

Faites également attention aux résultats vides des applets de commande. Les cmdlets ou pipelines qui n’ont aucune sortie sont traités comme un tableau vide qui ne correspond à rien, y compris dans le cas du default.

$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
    $null   { '$file is $null' }
    default { "`$file is type $($file.GetType().Name)" }
}
# No matches

Expression constante

Lee Dailey a souligné que nous pouvons utiliser une expression constante $true pour évaluer [bool] les éléments. Imaginez si plusieurs vérifications booléennes doivent se produire.

$isVisible = $false
$isEnabled = $true
$isSecure = $true

switch ( $true )
{
    $isEnabled
    {
        'Do-Action'
    }
    $isVisible
    {
        'Show-Animation'
    }
    $isSecure
    {
        'Enable-AdminMenu'
    }
}
Do-Action
Enabled-AdminMenu

Il s’agit d’un moyen propre d’évaluer et d’agir sur l’état de plusieurs champs booléens. L'intérêt de cela, c'est qu'une correspondance peut basculer l'état d'une valeur qui n'a pas encore été évaluée.

$isVisible = $false
$isEnabled = $true
$isAdmin = $false

switch ( $true )
{
    $isEnabled
    {
        'Do-Action'
        $isVisible = $true
    }
    $isVisible
    {
        'Show-Animation'
    }
    $isAdmin
    {
        'Enable-AdminMenu'
    }
}
Do-Action
Show-Animation

Définir $isEnabled à $true dans cet exemple garantit que $isVisible est également réglé à $true. Ensuite, lorsque $isVisible est évalué, le bloc d'instructions correspondant est appelé. C’est un peu contre-intuitif, mais c’est une utilisation intelligente de la mécanique.

variable automatique $switch

Lorsque le switch traitement de ses valeurs est en cours, il crée un énumérateur et l’appelle $switch. Il s’agit d’une variable automatique créée par PowerShell et vous pouvez la manipuler directement.

$a = 1, 2, 3, 4

switch($a) {
    1 { [void]$switch.MoveNext(); $switch.Current }
    3 { [void]$switch.MoveNext(); $switch.Current }
}

Cela vous donne les résultats des éléments suivants :

2
4

En déplaçant l’énumérateur vers l’avant, l’élément suivant n’est pas traité par la switch valeur, mais vous pouvez accéder directement à cette valeur. Je l’appellerais la folie.

Autres modèles

Tables de hachage

L’un de mes articles les plus populaires est celui que j’ai fait sur les tables de hachage. L’un des cas d'usage d’un hashtable est d'être une table de correspondance. Il s’agit d’une autre approche d’un modèle commun qu’une switch instruction traite souvent.

$day = 3

$lookup = @{
    0 = 'Sunday'
    1 = 'Monday'
    2 = 'Tuesday'
    3 = 'Wednesday'
    4 = 'Thursday'
    5 = 'Friday'
    6 = 'Saturday'
}

$lookup[$day]
Wednesday

Si je n’utilise un switch que pour consultation, j’utilise souvent un hashtable à la place.

Enum

PowerShell 5.0 a introduit le enum, et c'est également une option dans ce cas.

$day = 3

enum DayOfTheWeek {
    Sunday
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
}

[DayOfTheWeek]$day
Wednesday

Nous pourrions aller toute la journée à la recherche de différentes façons de résoudre ce problème. Je voulais juste vous assurer que vous aviez des options.

Mots finaux

L’instruction switch est simple sur la surface, mais elle offre certaines fonctionnalités avancées que la plupart des gens ne réalisent pas sont disponibles. Combiner ces fonctionnalités en fait une fonctionnalité puissante. J’espère que vous avez appris quelque chose que vous n’aviez pas réalisé auparavant.