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 Component
Role
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 foreach
continue
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. break
detiene 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 switch
está 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.