Compartir a través de


Capítulo 6: Control de flujo

Escritura de scripts

Al pasar de escribir líneas de comando de PowerShell a escribir scripts, puede parecer más complicado de lo que realmente es. Un script no es más que los mismos comandos o similares que se ejecutan de forma interactiva en la consola de PowerShell, excepto que los guarda como un archivo .ps1. Hay algunas construcciones de scripting que puede usar, como un bucle foreach en lugar del cmdlet ForEach-Object. Las diferencias pueden resultar confusas para principiantes al considerar que foreach es una palabra clave de lenguaje y un alias para el cmdlet ForEach-Object.

Bucle

Uno de los mejores aspectos de PowerShell es su escalabilidad. Una vez que aprenda a realizar una tarea para un solo elemento, aplicar la misma acción a cientos de elementos es casi tan sencillo. Recorra los elementos mediante uno de los distintos tipos de bucles en PowerShell.

ForEach-Object

ForEach-Object es un cmdlet para iterar los elementos de una canalización como, por ejemplo, con las secuencias de una frase de PowerShell. ForEach-Object transmite los objetos a través de la canalización.

Aunque el parámetro del módulo de Get-Command acepta varios valores de cadena, solo los acepta por entrada de canalización por nombre de propiedad. En el siguiente escenario, si desea pasar dos valores de cadena a Get-Command para su uso con el parámetro del Módulo, debe usar el cmdlet ForEach-Object.

'ActiveDirectory', 'SQLServer' |
    ForEach-Object {Get-Command -Module $_} |
    Group-Object -Property ModuleName -NoElement |
    Sort-Object -Property Count -Descending
Count Name
----- ----
  147 ActiveDirectory
   82 SqlServer

En el ejemplo anterior, $_ es el objeto actual. A partir de la versión 3.0 de PowerShell, se puede usar $PSItem en lugar de $_. La mayoría de los usuarios experimentados de PowerShell prefieren usar $_, ya que es compatible con versiones anteriores y requiere menos que teclear.

Al usar la palabra clave foreach, debe almacenar los elementos en la memoria antes de recorrerlos en iteración, lo que podría ser difícil si no sabe con cuántos elementos está trabajando.

$ComputerName = 'DC01', 'WEB01'
foreach ($Computer in $ComputerName) {
    Get-ADComputer -Identity $Computer
}
DistinguishedName : CN=DC01,OU=Domain Controllers,DC=mikefrobbins,DC=com
DNSHostName       : dc01.mikefrobbins.com
Enabled           : True
Name              : DC01
ObjectClass       : computer
ObjectGUID        : c38da20c-a484-469d-ba4c-bab3fb71ae8e
SamAccountName    : DC01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1001
UserPrincipalName :

DistinguishedName : CN=WEB01,CN=Computers,DC=mikefrobbins,DC=com
DNSHostName       : web01.mikefrobbins.com
Enabled           : True
Name              : WEB01
ObjectClass       : computer
ObjectGUID        : 33aa530e-1e31-40d8-8c78-76a18b673c33
SamAccountName    : WEB01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1107
UserPrincipalName :

Muchas veces se necesita un bucle como foreach o ForEach-Object. De lo contrario, recibirá un mensaje de error.

Get-ADComputer -Identity 'DC01', 'WEB01'
Get-ADComputer : Cannot convert 'System.Object[]' to the type
'Microsoft.ActiveDirectory.Management.ADComputer' required by parameter
'Identity'. Specified method is not supported.
At line:1 char:26
+ Get-ADComputer -Identity 'DC01', 'WEB01'
+                          ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-ADComputer], Parame
   terBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirecto
   ry.Management.Commands.GetADComputer

Otras veces, puede obtener los mismos resultados al eliminar el bucle. Consulte la ayuda del cmdlet para comprender las opciones.

'DC01', 'WEB01' | Get-ADComputer
DistinguishedName : CN=DC01,OU=Domain Controllers,DC=mikefrobbins,DC=com
DNSHostName       : dc01.mikefrobbins.com
Enabled           : True
Name              : DC01
ObjectClass       : computer
ObjectGUID        : c38da20c-a484-469d-ba4c-bab3fb71ae8e
SamAccountName    : DC01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1001
UserPrincipalName :

DistinguishedName : CN=WEB01,CN=Computers,DC=mikefrobbins,DC=com
DNSHostName       : web01.mikefrobbins.com
Enabled           : True
Name              : WEB01
ObjectClass       : computer
ObjectGUID        : 33aa530e-1e31-40d8-8c78-76a18b673c33
SamAccountName    : WEB01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1107
UserPrincipalName :

Como puede ver en los ejemplos anteriores, el parámetro Identity para Get-ADComputer solo acepta un valor único cuando se proporciona a través de la entrada de parámetros. Sin embargo, mediante la canalización, puede enviar varios valores al comando porque los valores se procesan de uno en uno.

Para

Un bucle for recorre en iteración mientras se cumple una condición especificada. No uso el bucle for a menudo, pero tiene usos.

for ($i = 1; $i -lt 5; $i++) {
    Write-Output "Sleeping for $i seconds"
    Start-Sleep -Seconds $i
}
Sleeping for 1 seconds
Sleeping for 2 seconds
Sleeping for 3 seconds
Sleeping for 4 seconds

En el ejemplo anterior, el bucle recorre en iteración cuatro veces empezando por el número uno y continuando siempre que la variable de contador $i sea inferior a 5. Se duerme durante un total de 10 segundos.

Qué hacer

Hay dos bucles do diferentes en PowerShell: do until y do while. do until se ejecuta hasta que la condición especificada es falsa.

El ejemplo siguiente es un juego de números que continúa hasta que el valor que adivina es igual al mismo número que el cmdlet Get-Random generado.

$number = Get-Random -Minimum 1 -Maximum 10
do {
    $guess = Read-Host -Prompt "What's your guess?"
    if ($guess -lt $number) {
        Write-Output 'Too low!'
    } elseif ($guess -gt $number) {
        Write-Output 'Too high!'
    }
}
until ($guess -eq $number)
What's your guess?: 1
Too low!
What's your guess?: 2
Too low!
What's your guess?: 3

Do While es lo contrario. Se ejecuta siempre que la condición especificada se evalúe como true.

$number = Get-Random -Minimum 1 -Maximum 10
do {
    $guess = Read-Host -Prompt "What's your guess?"
    if ($guess -lt $number) {
        Write-Output 'Too low!'
    } elseif ($guess -gt $number) {
        Write-Output 'Too high!'
    }
}
while ($guess -ne $number)
What's your guess?: 1
Too low!
What's your guess?: 2
Too low!
What's your guess?: 3
Too low!
What's your guess?: 4

Se logran los mismos resultados con un bucle Do While invirtiendo la condición de prueba en no es igual a.

Los bucles do se ejecutan siempre al menos una vez porque la condición se evalúa al final del bucle.

Mientras

Al igual que el bucle do while, se ejecuta un bucle while siempre que la condición especificada sea verdadera. Sin embargo, la diferencia es que un bucle while evalúa la condición en la parte superior del bucle antes de que se ejecute cualquier código. Por lo tanto, no se ejecuta si la condición se evalúa como false.

En el ejemplo siguiente se calcula el día de Acción de Gracias en los Estados Unidos. Siempre está el cuarto jueves de noviembre. El bucle comienza con el día 22 de noviembre y agrega un día, mientras que el día de la semana no es igual al jueves. Si el 22 es un jueves, el bucle no se ejecuta en absoluto.

$date = Get-Date -Date 'November 22'
while ($date.DayOfWeek -ne 'Thursday') {
    $date = $date.AddDays(1)
}
Write-Output $date
Thursday, November 23, 2017 12:00:00 AM

break, continue y return

La palabra clave break está diseñada para salir de un bucle y a menudo se usa con la instrucción switch. En el ejemplo siguiente, break hace que el bucle finalice después de la primera iteración.

for ($i = 1; $i -lt 5; $i++) {
    Write-Output "Sleeping for $i seconds"
    Start-Sleep -Seconds $i
    break
}
Sleeping for 1 seconds

La palabra clave continue está diseñada para ir directamente a la siguiente iteración de un bucle.

En el ejemplo siguiente se generan los números 1, 2, 4 y 5. Omite el número 3 y continúa con la siguiente iteración del bucle. De forma similar a break, continue sale del bucle, salvo que solo lo hace en la iteración actual. La ejecución continúa con la siguiente iteración en lugar de interrumpir el bucle por completo y detenerlo.

while ($i -lt 5) {
    $i += 1
    if ($i -eq 3) {
        continue
    }
    Write-Output $i
}
1
2
4
5

La palabra clave return está diseñada para salir del ámbito existente.

Observe en el ejemplo siguiente que return genera el primer resultado y, a continuación, sale del bucle.

$number = 1..10
foreach ($n in $number) {
    if ($n -ge 4) {
        return $n
    }
}
4

Se puede encontrar una explicación más detallada de la declaración de resultado en uno de mis artículos de blog: La palabra clave return de PowerShell.

Resumen

En este capítulo, ha obtenido información sobre los distintos tipos de bucles que existen en PowerShell.

Revisión

  1. ¿Cuál es la diferencia entre el cmdlet ForEach-Object y la instrucción foreach?
  2. ¿Cuál es la principal ventaja de usar un bucle while en lugar de un bucle do while o do until?
  3. ¿Cómo difieren las instrucciones break y continue?

Referencias