Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Todo lo que quieres saber sobre la
Al igual que muchos otros lenguajes, PowerShell tiene instrucciones para ejecutar código condicionalmente en los scripts. Una de esas declaraciones es la instrucción If. Hoy se profundizará en uno de los comandos más fundamentales de PowerShell.
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. Consulte su blog en PowerShellExplained.com.
Ejecución condicional
Tus scripts a menudo necesitan tomar decisiones y realizar una lógica diferente basada en esas decisiones.
Esto es lo que quiero decir mediante la ejecución condicional. Tiene una instrucción o valor para evaluar y, a continuación, ejecutar una sección diferente del código en función de esa evaluación. Esto es exactamente lo que hace la if instrucción.
Instrucción if
Este es un ejemplo básico de la if instrucción :
$condition = $true
if ( $condition )
{
Write-Output "The condition was true"
}
Lo primero que hace la instrucción if es evaluar la expresión entre paréntesis. Si se evalúa como $true, ejecuta las instrucciones en las llaves. Si el valor era $false, omitiría ese bloque de instrucciones.
En el ejemplo anterior, la if instrucción acaba de evaluar la $condition variable. Era $true y habría ejecutado el Write-Output comando dentro del bloque de instrucciones.
En algunos lenguajes, puede colocar una sola línea de código después de la if instrucción y se ejecuta. No es el caso de PowerShell. Debe proporcionar una llave completa con llaves statement block para que funcione correctamente.
Operadores de comparación
El uso más común de la if instrucción es para comparar dos elementos entre sí. PowerShell tiene operadores especiales para diferentes escenarios de comparación. Cuando se usa un operador de comparación, el valor del lado izquierdo se compara con el valor del lado derecho.
-eq para igualdad
-eq realiza una comprobación de igualdad entre dos valores para asegurarse de que son iguales entre sí.
$value = Get-MysteryValue
if ( 5 -eq $value )
{
# do something
}
En este ejemplo, estoy tomando un valor conocido de 5 y comparándolo con mi $value para ver si coinciden.
Un caso de uso posible es comprobar el estado de un valor antes de realizar una acción en él. Puede obtener un servicio y comprobar que el estado estaba en ejecución antes de llamarlo Restart-Service.
Es habitual en otros lenguajes como C# usar == para la igualdad (por ejemplo, 5 == $value), pero eso no funciona con PowerShell. Otro error común que hacen las personas es usar el signo igual (por ejemplo: 5 = $value) que está reservado para asignar valores a variables. Al colocar su valor conocido en la izquierda, hace que ese error sea más difícil de cometer.
Este operador (así como otros) tiene algunas variaciones.
-
-eqIgualdad sin distinción entre mayúsculas y minúsculas -
-ieqIgualdad sin distinción entre mayúsculas y minúsculas -
-ceqIgualdad con distinción entre mayúsculas y minúsculas
-ne no es igual
Muchos operadores tienen un operador relacionado que comprueba si hay un resultado opuesto.
-ne comprueba que los valores no son iguales entre sí.
if ( 5 -ne $value )
{
# do something
}
Úselo para asegurarse de que la acción solo se ejecuta si el valor no es 5. Un buen caso de uso donde sería comprobar si un servicio estaba en estado de ejecución antes de intentar iniciarlo.
Variaciones:
-
-neinsensible a mayúsculas no igual -
-inesin distinción entre mayúsculas y minúsculas no es igual a -
-cneDistinguir mayúsculas de minúsculas no es igual a
Estas son variaciones inversas de -eq. Agruparé estos tipos cuando enumere variaciones para otros operadores.
-gt (mayor que), -ge (mayor o igual), -lt (menor que), -le (menor o igual) para mayor que o menor que
Estos operadores se usan al comprobar si un valor es mayor o menor que otro valor.
El -gt -ge -lt -le representa GreaterThan, GreaterThanOrEqual, LessThan y LessThanOrEqual.
if ( $value -gt 5 )
{
# do something
}
Variaciones:
-
-gtmayor que -
-igtmayor que, insensible a mayúsculas -
-cgtmayor que, distingue mayúsculas de minúsculas -
-gemayor o igual que -
-igemayor o igual, insensible a mayúsculas -
-cgemayor o igual, sensible a mayúsculas y minúsculas -
-ltmenor que -
-iltmenor que, insensible a mayúsculas y minúsculas -
-cltmenor que, sensible a mayúsculas y minúsculas -
-lemenor o igual que -
-ilemenor o igual, sin distinción de mayúsculas -
-clemenor o igual que, sensible a mayúsculas
No sé por qué usaría opciones que distinguen mayúsculas de minúsculas y no distinguen mayúsculas de minúsculas para estos operadores.
Coincidencias de tipo comodín
PowerShell tiene su propia sintaxis de coincidencia de patrones basada en caracteres comodín y puede usarla con el -like operador . Estos patrones de caracteres comodín son bastante básicos.
-
?coincide con cualquier carácter único -
*coincide con cualquier número de caracteres
$value = 'S-ATX-SQL01'
if ( $value -like 'S-*-SQL??')
{
# do something
}
Es importante señalar que el patrón coincide con toda la cadena. Si necesita hacer coincidir algo en el centro de la cadena, debe colocar * en ambos extremos de la cadena.
$value = 'S-ATX-SQL02'
if ( $value -like '*SQL*')
{
# do something
}
Variaciones:
-
-likecaracteres comodín que no distinguen mayúsculas de minúsculas -
-ilikecaracteres comodín que no distinguen mayúsculas de minúsculas -
-clikeCaracteres comodín que distinguen mayúsculas de minúsculas -
-notlikeCarácter comodín que no distingue entre mayúsculas y minúsculas no coincide -
-inotlikeNo coincide con el carácter comodín que no distingue mayúsculas de minúsculas -
-cnotlikeCaracteres comodín con distinción entre mayúsculas y minúsculas no coincide
-coincidir expresión regular
El -match operador permite comprobar una cadena para una coincidencia basada en expresiones regulares. Úselo cuando los patrones de caracteres comodín no sean lo suficientemente flexibles para usted.
$value = 'S-ATX-SQL01'
if ( $value -match 'S-\w\w\w-SQL\d\d')
{
# do something
}
Un patrón regex coincide con cualquier parte de la cadena de forma predeterminada. Por lo tanto, puede especificar una subcadena que desee que coincida de la siguiente manera:
$value = 'S-ATX-SQL01'
if ( $value -match 'SQL')
{
# do something
}
Regex es un lenguaje complejo propio y merece la pena examinar. Hablo más sobre -match y las muchas maneras de usar regex en otro artículo.
Variaciones:
-
-matchregex sin distinción entre mayúsculas y minúsculas -
-imatchregex sin distinción entre mayúsculas y minúsculas -
-cmatchregex sensible a mayúsculas y minúsculas -
-notmatchRegex sin distinción entre mayúsculas y minúsculas no coincide -
-inotmatchRegex sin distinción entre mayúsculas y minúsculas no encontró coincidencias -
-cnotmatchRegex con distinción entre mayúsculas y minúsculas no coincide
es de tipo
Puede comprobar el tipo de un valor con el -is operador .
if ( $value -is [string] )
{
# do something
}
Puede usarlo si está trabajando con clases o aceptando varios objetos a través de la canalización. Puede introducir un servicio o un nombre de servicio como entrada. A continuación, compruebe si tiene un servicio y obtenga el servicio si solo tiene el nombre.
if ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
$Service = Get-Service -Name $Service
}
Variaciones:
-
-isde tipo -
-isnotno de tipo
Operadores de recopilación
Cuando se usan los operadores anteriores con un valor único, el resultado es $true o $false. Esto se controla de forma ligeramente diferente al trabajar con una colección. Cada elemento de la colección se evalúa y el operador devuelve todos los valores que se evalúan como $true.
PS> 1,2,3,4 -eq 3
3
Esto sigue funcionando correctamente en una if sentencia. Por lo tanto, el operador devuelve un valor y, a continuación, toda la instrucción es $true.
$array = 1..6
if ( $array -gt 3 )
{
# do something
}
Hay una pequeña trampa oculta en los detalles aquí que necesito señalar. Al usar el operador -ne de esta manera, es fácil entender erróneamente la lógica al revés. Usar -ne con una colección devuelve $true si algún elemento de la colección no coincide con tu valor.
PS> 1,2,3 -ne 4
1
2
3
Esto puede parecer un truco inteligente, pero tenemos operadores -contains y -in que controlan esto de forma más eficaz. Y -notcontains hace lo que esperas.
-contiene
El -contains operador comprueba la colección para su valor. Tan pronto como encuentre una coincidencia, devuelve $true.
$array = 1..6
if ( $array -contains 3 )
{
# do something
}
Esta es la manera preferida de comprobar si una colección contiene tu valor. El uso de Where-Object (o -eq) recorre toda la lista cada vez y es significativamente más lento.
Variaciones:
-
-containsCoincidencia que no distingue mayúsculas de minúsculas -
-icontainsCoincidencia que no distingue mayúsculas de minúsculas -
-ccontainsCoincidencia sensible a mayúsculas y minúsculas -
-notcontainsinsensible a mayúsculas no coincide -
-inotcontainsno coincide por insensible a mayúsculas -
-cnotcontainsNo coincide debido a distinción de mayúsculas y minúsculas
-in
El -in operador es igual que el -contains operador, excepto que la colección está en el lado derecho.
$array = 1..6
if ( 3 -in $array )
{
# do something
}
Variaciones:
-
-inCoincidencia que no distingue mayúsculas de minúsculas -
-iinCoincidencia insensible a mayúsculas y minúsculas -
-cinCoincidencia sensible a mayúsculas y minúsculas -
-notinsin distinción entre mayúsculas y minúsculas no coincide -
-inotinsin distinción entre mayúsculas y minúsculas no coincide -
-cnotinNo hay coincidencia sensible a mayúsculas y minúsculas
Operadores lógicos
Los operadores lógicos se usan para invertir o combinar otras expresiones.
no
El -not operador voltea una expresión de $false a $true o de $true a $false. Este es un ejemplo en el que queremos realizar una acción cuando Test-Path es $false.
if ( -not ( Test-Path -Path $path ) )
La mayoría de los operadores de las que hablamos tienen una variante donde no es necesario usar el operador -not. Pero todavía hay veces que es útil.
! operator
Puede usar ! como alias para -not.
if ( -not $value ){}
if ( !$value ){}
Es posible que ! sea usado más por personas que vienen de otros lenguajes como C#. Prefiero escribirlo porque me resulta difícil ver al mirar rápidamente mis scripts.
-y
Puede combinar expresiones con el -and operador . Al hacerlo, ambos lados deben ser $true para que toda la expresión sea $true.
if ( ($age -gt 13) -and ($age -lt 55) )
En ese ejemplo, $age debe ser igual o superior a 13 para el lado izquierdo y menos de 55 para el lado derecho. He agregado paréntesis adicionales para que sea más claro en ese ejemplo, pero son opcionales siempre que la expresión sea sencilla. Este es el mismo ejemplo sin ellos.
if ( $age -gt 13 -and $age -lt 55 )
La evaluación se produce de izquierda a derecha. Si el primer elemento se evalúa como $false, sale temprano y no realiza la comparación correcta. Esto es útil cuando necesita asegurarse de que existe un valor antes de usarlo. Por ejemplo, Test-Path produce un error si se le asigna una $null ruta de acceso.
if ( $null -ne $path -and (Test-Path -Path $path) )
-o
-or permite especificar dos expresiones y devuelve $true si alguna de ellas es $true.
if ( $age -le 13 -or $age -ge 55 )
Al igual que con el -and operador , la evaluación se produce de izquierda a derecha. Excepto que si la primera parte es $true, la instrucción completa es $true y no procesa el resto de la expresión.
Tome nota también de cómo funciona la sintaxis para estos operadores. Necesitas dos expresiones independientes. He visto que los usuarios intentan hacer algo parecido a esto $value -eq 5 -or 6 sin darse cuenta de su error.
-xor exclusivo o
Esta es un poco inusual.
-xor permite que solo una expresión se evalúe como $true. Por lo tanto, si ambos elementos son $false o ambos elementos son $true, la expresión completa es $false. Otra manera de examinar esto es que la expresión es solo $true si los resultados de la expresión son diferentes.
Es raro que alguien alguna vez usara este operador lógico y no puedo pensar en un buen ejemplo de por qué nunca lo usaría.
Operadores bit a bit
Los operadores bit a bit realizan cálculos en los bits dentro de los valores y generan un nuevo valor como resultado. La enseñanza de operadores bit a bit está fuera del ámbito de este artículo, pero esta es la lista de ellos.
-
-bandY binario -
-borOR binario -
-bxorOR exclusivo binario -
-bnotno binario -
-shldesplazamiento a la izquierda -
-shrdesplazar hacia la derecha
Expresiones de PowerShell
Podemos usar PowerShell normal dentro de la instrucción de condición.
if ( Test-Path -Path $Path )
Test-Path devuelve $true o $false cuando se ejecuta. Esto también se aplica a los comandos que devuelven otros valores.
if ( Get-Process Notepad* )
Se evalúa como $true si hay un proceso devuelto y $false si no lo hay. Es perfectamente válido usar expresiones de canalización u otras instrucciones de PowerShell como esta:
if ( Get-Process | where Name -EQ Notepad )
Estas expresiones se pueden combinar entre sí con los -and operadores y -or , pero es posible que tenga que usar paréntesis para dividirlas en subexpresiones.
if ( (Get-Process) -and (Get-Service) )
Comprobación de $null
Cuando no hay resultado o un valor $null se evalúa como $false en la instrucción if. Al comprobar específicamente para $null, es una buena práctica colocar $null en el lado izquierdo.
if ( $null -eq $value )
Hay bastantes matices al tratar con los valores de $null en PowerShell. Si estás interesado en profundizar más, tengo un artículo sobre todo lo que querías saber sobre $null.
Asignación de variables dentro de la condición
Casi olvidé agregar este hasta que Prasoon Karunan V me recordó de ello.
if ($process=Get-Process notepad -ErrorAction Ignore) {$process} else {$false}
Normalmente, cuando se asigna un valor a una variable, el valor no se pasa a la canalización o consola. Cuando realizas una asignación de variable en una subexpresión, esta se transmite a la canalización.
PS> $first = 1
PS> ($second = 2)
2
Vea cómo la $first asignación no tiene ninguna salida y la $second asignación sí? Cuando se realiza una asignación en una if instrucción , se ejecuta igual que la $second asignación anterior. Este es un ejemplo limpio sobre cómo puede usarlo:
if ( $process = Get-Process Notepad* )
{
$process | Stop-Process
}
Si $process se le asigna un valor, la instrucción es $true y $process se detiene.
Asegúrese de no confundir esto con -eq, ya que no es una comprobación de igualdad. Esta es una característica más oscura que la mayoría de las personas no se dan cuenta de que funciona de esta manera.
Asignación de variables desde el bloque de instrucciones
También puede usar el bloque de instrucciones if para asignar un valor a una variable.
$discount = if ( $age -ge 55 )
{
Get-SeniorDiscount
}
elseif ( $age -le 13 )
{
Get-ChildDiscount
}
else
{
0.00
}
Cada bloque de script escribe los resultados de los comandos o el valor, como salida. Podemos asignar el resultado de la if declaración a la $discount variable. Ese ejemplo podría haber asignado tan fácilmente esos valores directamente a la variable $discount en cada bloque de instrucciones. No puedo decir que uso esto con la if instrucción a menudo, pero tengo un ejemplo en el que usé esto recientemente.
Ruta alternativa de ejecución
La if instrucción permite especificar una acción para no solo cuando la instrucción es $true, sino también para cuando es $false. Aquí es donde entra en juego la else declaración.
más
La else instrucción es siempre la última parte de la if instrucción al utilizarse.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
Write-Warning "$path doesn't exist or isn't a file."
}
En este ejemplo, comprobamos el $path para asegurarnos de que es un archivo. Si encontramos el archivo, lo movemos. Si no es así, se escribe una advertencia. Este tipo de lógica de bifurcación es muy común.
Anidado si
Las if instrucciones y else toman un bloque de script, por lo que podemos colocar cualquier comando de PowerShell dentro de ellos, incluida otra if instrucción. Esto le permite usar una lógica mucho más complicada.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
if ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
}
En este ejemplo, primero probamos el camino feliz y luego tomamos medidas al respecto. Si se produce un error, realizamos otra comprobación y para proporcionar información más detallada al usuario.
elseif
No estamos limitados a una sola comprobación condicional. Podemos encadenar if instrucciones y else instrucciones en lugar de anidarlas mediante la elseif instrucción.
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
elseif ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
La ejecución se produce desde la parte superior hasta la parte inferior. La instrucción principal if se evalúa primero. Si es $false, se mueve hacia abajo hasta el siguiente elseif o else en la lista. Esta última else es la acción predeterminada que se debe realizar si ninguno de los demás devuelve $true.
modificador
En este momento, necesito mencionar la switch declaración. Proporciona una sintaxis alternativa para realizar varias comparaciones con un valor.
switchCon , se especifica una expresión y ese resultado se compara con varios valores diferentes. Si uno de esos valores coincide, se ejecuta el bloque de código coincidente. Eche un vistazo a este ejemplo:
$itemType = 'Role'
switch ( $itemType )
{
'Component'
{
'is a component'
}
'Role'
{
'is a role'
}
'Location'
{
'is a location'
}
}
Hay tres valores posibles que pueden coincidir con .$itemType En este caso, coincide con Role. He usado un ejemplo sencillo solo para darle cierta exposición al switch operador. Hablo más sobre todo lo que nunca quería saber sobre la instrucción switch en otro artículo.
Matriz en línea
Tengo una función llamada Invoke-SnowSql que inicia un ejecutable con varios argumentos de línea de comandos. Este es un clip de esa función donde se compila la matriz de argumentos.
$snowSqlParam = @(
'--accountname', $Endpoint
'--username', $Credential.UserName
'--option', 'exit_on_error=true'
'--option', 'output_format=csv'
'--option', 'friendly=false'
'--option', 'timing=false'
if ($Debug)
{
'--option', 'log_level=DEBUG'
}
if ($Path)
{
'--filename', $Path
}
else
{
'--query', $singleLineQuery
}
)
Las $Debug variables y $Path son parámetros de la función proporcionada por el usuario final.
Los valoro en línea dentro de la inicialización de mi matriz. Si $Debug es verdadero, esos valores se ubican en $snowSqlParam en el lugar correcto. Lo mismo se aplica a la variable $Path.
Simplificación de operaciones complejas
Es inevitable encontrarse con una situación que tenga demasiadas comparaciones para verificar y la instrucción if se desplaza hacia la derecha de la pantalla.
$user = Get-ADUser -Identity $UserName
if ( $null -ne $user -and $user.Department -eq 'Finance' -and $user.Title -match 'Senior' -and $user.HomeDrive -notlike '\\server\*' )
{
# Do Something
}
Pueden ser difíciles de leer y que te hacen más propensos a cometer errores. Hay algunas cosas que podemos hacer sobre eso.
Continuación de línea
Hay algunos operadores en PowerShell que le permiten encapsular el comando en la línea siguiente. Los operadores lógicos -and y -or son buenos operadores que se usarán si desea dividir la expresión en varias líneas.
if ($null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
)
{
# Do Something
}
Todavía hay mucho que sucede allí, pero poner cada pieza en su propia línea marca una gran diferencia. Por lo general, uso esto cuando obtengo más de dos comparaciones o si tengo que desplazarme a la derecha para leer cualquiera de la lógica.
Cálculo previo de los resultados
Podemos sacar esa declaración de la if y comprobar solo el resultado.
$needsSecureHomeDrive = $null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
if ( $needsSecureHomeDrive )
{
# Do Something
}
Esto simplemente se siente mucho más limpio que el ejemplo anterior. También se le da la oportunidad de usar un nombre de variable que describa lo que realmente está verificando. Esto también es y ejemplo de código autodocumentado que guarda comentarios innecesarios.
Varias instrucciones if
Podemos descomponer esto en varios enunciados y comprobarlos uno a uno. En este caso, se usa una marca o una variable de seguimiento para combinar los resultados.
$skipUser = $false
if( $null -eq $user )
{
$skipUser = $true
}
if( $user.Department -ne 'Finance' )
{
Write-Verbose "isn't in Finance department"
$skipUser = $true
}
if( $user.Title -match 'Senior' )
{
Write-Verbose "Doesn't have Senior title"
$skipUser = $true
}
if( $user.HomeDrive -like '\\server\*' )
{
Write-Verbose "Home drive already configured"
$skipUser = $true
}
if ( -not $skipUser )
{
# do something
}
Tuve que invertir la lógica para que el funcionamiento de la bandera sea correcto. Cada evaluación es una declaración individual if. La ventaja de esto es que, al depurar, puede indicar exactamente lo que hace la lógica. Pude añadir una verbosidad mucho mejor al mismo tiempo.
La desventaja obvia es que es mucho más código que escribir. El código es más complejo de examinar, ya que toma una sola línea de lógica y la explota en 25 o más líneas.
Uso de funciones
También podemos mover toda esa lógica de validación a una función. Mira lo limpio que se ve esto de un vistazo.
if ( Test-SecureDriveConfiguration -ADUser $user )
{
# do something
}
Todavía tiene que crear la función para realizar la validación, pero facilita mucho el trabajo con este código. Facilita la prueba de este código. En tus pruebas, puedes simular la llamada a Test-ADDriveConfiguration y solo necesitas dos tests para esta función. Uno donde devuelve $true y otro donde devuelve $false. Probar la otra función es más sencilla porque es tan pequeña.
El cuerpo de esa función todavía podría ser esa línea con la que empezamos o la lógica desglosada que usamos en la última sección. Esto funciona bien en ambos escenarios y le permite cambiar fácilmente esa implementación más adelante.
Control de errores
Un uso importante de la if instrucción es comprobar si hay condiciones de error antes de que se produzcan errores. Un buen ejemplo es comprobar si ya existe una carpeta antes de intentar crearla.
if ( -not (Test-Path -Path $folder) )
{
New-Item -Type Directory -Path $folder
}
Me gusta decir que si esperas que se produzca una excepción, entonces no es realmente una excepción. Por lo tanto, compruebe los valores y valide las condiciones donde pueda.
Si quiere profundizar un poco más en el control de excepciones real, tengo un artículo sobre todo lo que alguna vez quería saber sobre las excepciones.
Palabras finales
La if instrucción es una instrucción tan sencilla, pero es una parte fundamental de PowerShell. Te encontrarás usando esto múltiples veces en casi todos los guiones que escribas. Espero que tengas una mejor comprensión de lo que tenías antes.