Articles

Todo lo que siempre quiso saber sobre la instrucción switch

  • 03/01/2021
  • 12 minutos de lectura
    • s
    • c
    • x

Al igual que muchos otros lenguajes, PowerShell tiene comandos para controlar el flujo de ejecución dentro de sus scripts. Una de esas instrucciones es la instrucción switch, que en PowerShell ofrece funciones que no se encuentran en otros idiomas. Hoy, profundizamos en el trabajo con PowerShellswitch.

Nota

La versión original de este artículo apareció en el blog escrito por @KevinMarquette. El equipo de Powershell agradece a Kevin por compartir este contenido con nosotros. Por favor, echa un vistazo a su blog atPowerShellExplained.com.

La instrucción if

Una de las primeras instrucciones que se aprenden es la instrucción if. Le permite ejecutar un bloque de script si una instrucción es $true.

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

Puede tener una lógica mucho más complicada utilizando instrucciones elseif y else. Aquí hay un ejemplo donde tengo un valor numérico para el día de la semana y quiero obtener el nombre como una cadena.

$day = 3if ( $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

resulta que este es un patrón común y hay muchas maneras de lidiar con esto. Uno de ellos es con un switch.

Instrucción Switch

La instrucciónswitch le permite proporcionar una variable y una lista de valores posibles. Si el valor coincide con la variable, se ejecuta su bloque de scripts.

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

en este ejemplo, el valor de $day coincide con uno de los valores numéricos, a continuación, el nombre correcto isassigned a $result. Solo estamos haciendo una asignación de variables en este ejemplo, pero cualquier PowerShell puede ejecutarse en esos bloques de script.

Asignar a una variable

Podemos escribir ese último ejemplo de otra manera.

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

Colocamos el valor en la canalización de PowerShell y lo asignamos a $result. Puede hacer lo mismo con las instrucciones if y foreach.

Predeterminado

Podemos usar la palabra clave default para identificar lo que debería suceder si no hay coincidencia.

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

Aquí devolvemos el valor Unknown en el caso predeterminado.

Cadenas

En esos últimos ejemplos, estaba haciendo coincidir números, pero también puedes hacer coincidir cadenas.

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

decidí no envuelva el ComponentRole y Location partidos en la cita aquí para resaltar thatthey son opcionales. El switch los trata como una cadena en la mayoría de los casos.

Matrices

Una de las características interesantes de PowerShell switch es la forma en que maneja las matrices. Si le da un array aswitch, procesa cada elemento de esa colección.

$roles = @('WEB','Database')switch ( $roles ) { 'Database' { 'Configure SQL' } 'WEB' { 'Configure IIS' } 'FileServer' { 'Configure Share' }}
Configure IISConfigure SQL

Si usted tiene elementos repetidos en su matriz, luego se cotejan varias veces por el appropriatesection.

PSItem

puede utilizar el $PSItem o $_ para hacer referencia al elemento actual que fue procesado. Cuando hacemos una coincidencia simple, $PSItem es el valor que coincidimos. Realizaré algunas coincidencias avanzadas en la siguiente sección donde se usa esta variable.

Parámetros

Una característica única de PowerShell switch es que tiene una serie de parámetros de conmutador que cambian su rendimiento.

– Las coincidencias no distinguen entre mayúsculas y minúsculas de forma predeterminada. Si necesita distinguir entre mayúsculas y minúsculas, puede usar-CaseSensitive. Esto se puede usar en combinación con los otros parámetros del interruptor.

– Comodín

Podemos habilitar el soporte de comodín con el conmutador -wildcard. Utiliza la misma lógica comodín que el operador-like para hacer cada coincidencia.

$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

Aquí estamos procesando un mensaje y luego enviándolo a diferentes flujos en función del contenido.

– Expresiones regulares

La instrucción switch admite coincidencias de expresiones regulares al igual que los comodines.

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

Tengo más ejemplos de uso de expresiones regulares en otro artículo que escribí: Las muchas formas de usar expresiones regulares.

– File

Una característica poco conocida de la instrucción switch es que puede procesar un archivo con el parámetro -File. Se usa -file con una ruta a un archivo en lugar de darle una expresión variable.

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

Funciona como procesar una matriz. En este ejemplo, lo combino con la coincidencia de comodines y uso $PSItem. Esto procesaría un archivo de registro y lo convertiría en mensajes de advertencia y errorm dependiendo de las coincidencias de expresiones regulares.

Detalles avanzados

Ahora que conoce todas estas características documentadas, podemos usarlas en el contexto de un procesamiento más avanzado.

Expresiones

El switch puede estar en una expresión en lugar de en una variable.

switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}

Cualquiera que sea la expresión a la que se evalúe es el valor utilizado para la coincidencia.

Múltiples coincidencias

Es posible que ya haya captado esto, pero un switch puede coincidir con múltiples condiciones. Esto es especialmente cierto cuando se usan coincidencias -wildcard o -regex. Puede agregar la misma condición varias veces y todas se activan.

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

Todas las tres de estas declaraciones son despedidos. Esto muestra que cada condición está marcada (en orden). Esto es válido para matrices de procesamiento en las que cada elemento comprueba cada condición.

Continuar

Normalmente, aquí es donde presentaría la instrucción break, pero es mejor que aprendamos cómo usar continue primero. Al igual que con un bucle foreachcontinue continúa en el siguiente elemento de la colección o sale del switch si no hay más elementos. Podemos reescribir ese último ejemplo con instrucciones continue para que solo se ejecute una instrucción.

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

en Lugar de la coincidencia de los tres elementos, el primero es emparejados y el interruptor sigue la nextvalue. Debido a que no quedan valores por procesar, el interruptor se cierra. El siguiente ejemplo muestra cómo un comodín puede coincidir con varios elementos.

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

Debido a que una línea en el archivo de entrada puede contener tanto la palabra Error como Warning, solo queremos que la primera se ejecute y luego continúe procesando el archivo.

Break

A break la instrucción sale del conmutador. Este es el mismo comportamiento que continue presenta para evaluaciones individuales. La diferencia se muestra al procesar una matriz. breakdetiene todo el procesamiento en el conmutador y continue pasa al siguiente elemento.

$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 updateWARNING: Ran into errors downloading filewrite-error -message $PSItem : Error: out of disk space+ CategoryInfo : NotSpecified: (:) , WriteErrorException+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException

En este caso, si llegamos a alguna de las líneas que comienzan con Error luego nos sale un error y el interruptor se detiene.Esto es lo que la instrucción break está haciendo por nosotros. Si encontramos Error dentro de la cadena y no solo al principio, lo escribimos como advertencia. Hacemos lo mismo con Warning. Es posible que una línea tenga la palabra Error y Warning, pero solo necesitamos un proceso. Esto es lo que la instrucción continue está haciendo por nosotros.

Romper las etiquetas

El switch declaración admite break/continue etiquetas como 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 } }}

Personalmente no me gusta el uso de etiquetas de ruptura, pero quería señalarlas porque se confunden si nunca las has visto antes. Cuando tiene varios estados switch o foreach que están anidados, es posible que desee salir de más del elemento más interno. Puede colocar una etiqueta a switch que puede ser el objetivo de su break.

Enumeración

PowerShell 5.0 nos dio enumeraciones y las podemos usar en un conmutador.

enum Context { Component Role Location}$item = ::Roleswitch ( $item ){ Component { 'is a component' } Role { 'is a role' } Location { 'is a location' }}
is a role

Si quieres mantener todo como inflexible de las enumeraciones, entonces usted puede colocar entre paréntesis.

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

Los paréntesis son necesarios aquí para que el conmutador no trate el valor ::Location como cadena literal.

Bloque de scripts

Podemos usar un bloque de scripts para realizar la evaluación de una coincidencia si es necesario.

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

Esto añade complejidad y puede hacer que su switch difícil de leer. En la mayoría de los casos en los que usaría algo como esto, sería mejor usar instrucciones if y elseif. Consideraría usar esto si ya tuviera un interruptor grande en su lugar y necesitara dos elementos para alcanzar el mismo bloque de evaluación.

Una cosa que creo que ayuda con la legibilidad es colocar el bloque de scripts entre paréntesis.

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

Todavía se ejecuta de la misma manera y proporciona un mejor descanso visual al mirarlo rápidamente.

Regex matches coincidencias

Necesitamos volver a la expresión regular para tocar algo que no es obvio de inmediato. El uso de regexpulsa la variable $matches. Entro en el uso de $matches más cuando hablo de las muchas formas de usar expresiones regulares. Aquí hay un ejemplo rápido para mostrarlo en acción con coincidencias con nombre.

$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-3456WARNING: message may contain a credit card number: 1234-5678-1234-5678

$null

Usted puede coincidir con un $null valor que no tiene que ser la predeterminada.

$value = $nullswitch ( $value ){ $null { 'Value is null' } default { 'value is not null' }}```OutputValue is null

Lo mismo ocurre con una cadena vacía.

switch ( '' ){ '' { 'Value is empty' } default { 'value is a empty string' }}```OutputValue is empty

Expresión constante

Lee Dailey señaló que podemos usar una expresión constante$true para evaluar elementos.Imagínese si tenemos que hacer varias comprobaciones booleanas.

$isVisible = $false$isEnabled = $true$isSecure = $trueswitch ( $true ){ $isEnabled { 'Do-Action' } $isVisible { 'Show-Animation' } $isSecure { 'Enable-AdminMenu' }}
Do-ActionEnabled-AdminMenu

Esta es una buena manera de evaluar y tomar decisiones sobre el estado de varios campos booleanos. Lo bueno de esto es que puedes hacer que una coincidencia cambie el estado de un valor que aún no se ha evaluado.

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

Configuración $isEnabled a $true en este ejemplo se asegura de que el $isVisible también$true. Luego, cuando se evalúa $isVisible, se invoca su bloque de scripts. Este es un contador de bits intuitivo, pero es un uso inteligente de la mecánica.

switch variable automática de conmutación

Cuando switchestá procesando sus valores, crea un enumerador y lo llama $switch. Esta es una variable automática creada por PowerShell y puede manipularla directamente.

$a = 1, 2, 3, 4switch($a) { 1 { $switch.MoveNext(); $switch.Current } 3 { $switch.MoveNext(); $switch.Current }}

Esto le da los resultados de:

24

Al mover el enumerador hacia adelante, el siguiente elemento no se procesa con switch pero puede acceder a ese valor directamente. Yo lo llamaría locura.

Otros patrones

Hashtables

Una de mis publicaciones más populares es la que hice en hashtables. Uno de los casos de uso para unhashtable es ser una tabla de búsqueda. Este es un enfoque alternativo a un patrón común que una instrucciónswitch a menudo está abordando.

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

Si sólo estoy usando un switch como una búsqueda, a menudo el uso de un hashtable en su lugar.

Enum

PowerShell 5.0 introdujo el Enum y es también una opción en este caso.

$day = 3enum DayOfTheWeek { Sunday Monday Tuesday Wednesday Thursday Friday Saturday}$day
Wednesday

podríamos seguir todo el día mirando a las diferentes maneras de resolver este problema. Sólo quería asegurarme de que sabías que tenías opciones.

Palabras finales

La instrucción switch es simple en la superficie, pero ofrece algunas características avanzadas que la mayoría de las personas no se dan cuenta de que están disponibles. Encadenar esas características juntas hace que esta sea una característica poderosa. Espero que hayas aprendido algo que no te habías dado cuenta antes.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *