about_ForEach

Se aplica a: Windows PowerShell 2.0, Windows PowerShell 3.0, Windows PowerShell 4.0, Windows PowerShell 5.0

TEMA

about_Foreach

DESCRIPCIÓN BREVE

Describe un comando de lenguaje que se puede usar para recorrer todos los elementos de una colección de elementos.

DESCRIPCIÓN LARGA

La instrucción Foreach (también conocida como un bucle Foreach) es una construcción de lenguaje para recorrer paso a paso (iterar) una serie de valores en una colección de elementos.

El tipo de colección más sencillo y más habitual que se recorre es una matriz. Dentro de un bucle Foreach, es frecuente ejecutar uno o más comandos en cada elemento de una matriz.

Sintaxis

A continuación se muestra la sintaxis de ForEach:

          foreach ($<item> in $<collection>){<statement list>}

Sintaxis simplificada

A partir de Windows PowerShell® 3.0, se simplificó la sintaxis con palabras clave del lenguaje, como Where y ForEach. Los operadores de comparación que funcionan en los miembros de una colección se tratan como parámetros. Puede usar un método en los miembros de una colección sin incluirlo en un bloque de script ni agregar la variable automática "$_.". Tenga en cuenta los dos ejemplos siguientes:

          dir cert:\ -Recurse | foreach GetKeyAlgorithm
          dir cert:\ -Recurse | foreach {$_.GetKeyAlgorithm()}

Aunque ambos comandos funcionan, el primero devuelve resultados sin usar un bloque de script ni la variable automática "$_.". El método GetKeyAlgorithm se trata como un parámetro de ForEach. El primer comando devuelve los mismos resultados, pero sin errores, ya que la sintaxis simplificada no intenta devolver resultados de elementos a los que no se aplicó el argumento especificado.

En este ejemplo, se pasa Description de la propiedad Get-Process como argumento de parámetro de la instrucción ForEach. Los resultados son las descripciones de los procesos activos.

          Get-Process | ForEach Description

La instrucción Foreach fuera de una canalización de comandos

La parte de la instrucción Foreach incluida entre paréntesis representa una variable y una colección que se va a iterar. Windows PowerShell crea la variable ($<item>) automáticamente cuando se ejecuta el bucle Foreach. Antes de cada iteración en el bucle, la variable se establece en un valor en la colección. El bloque después de una instrucción Foreach {<statement list>} contiene un conjunto de comandos que se va a ejecutar en cada elemento de una colección.

Ejemplos

Por ejemplo, en el ejemplo siguiente, el bucle Foreach muestra los valores de la matriz $letterArray.

        
          $letterArray = "a","b","c","d"
          foreach ($letter in $letterArray)
          {
              Write-Host $letter
          }

En este ejemplo, la matriz $letterArray se creó y se inicializó con los valores de cadena "a", "b", "c" y "d". La primera vez que se ejecuta la instrucción Foreach, establece la variable $letter igual al primer elemento de $letterArray ("a"). A continuación, usa el cmdlet Write-Host para mostrar la letra "a". La próxima vez en el bucle, $letter se establece en "b", y así sucesivamente. Después de que el bucle Foreach muestre la letra "d", Windows PowerShell sale del bucle.

La instrucción Foreach debe aparecer entera en una sola línea para que se ejecute como un comando en el símbolo del sistema de Windows PowerShell. No es necesario que la instrucción Foreach aparezca entera en una sola línea si el comando se incluye en un archivo de script. ps1.

Las instrucciones Foreach también pueden usarse junto con los cmdlets que devuelven una colección de elementos. En el ejemplo siguiente, la instrucción Foreach recorre paso a paso la lista de elementos que se devuelve mediante el cmdlet Get-ChildItem.

          foreach ($file in Get-ChildItem)
          {
              Write-Host $file
          }

Puede refinar el ejemplo usando una instrucción If para limitar los resultados que se devuelven. En el ejemplo siguiente, la instrucción Foreach realiza la misma operación de bucle que el ejemplo anterior, pero agrega una instrucción If para limitar los resultados a los archivos mayores de 100 kilobytes (KB):

          foreach ($file in Get-ChildItem)
          {
              if ($file.length -gt 100KB) 
              {
                  Write-Host $file
              }
          }

En este ejemplo, el bucle Foreach usa una propiedad de la variable $file para realizar una operación de comparación ($file.length -gt 100KB). La variable $file contiene todas las propiedades del objeto devuelto por el cmdlet Get-ChildItem. Por lo tanto, puede devolver más de un nombre de archivo. En el ejemplo siguiente, Windows PowerShell devuelve la longitud y la hora del último acceso en la lista de instrucciones:

          foreach ($file in Get-ChildItem)
          {
              if ($file.length -gt 100KB) 
              {
                  Write-Host $file
                  Write-Host $file.length
                  Write-Host $file.lastaccesstime
              }
          }

En este ejemplo, no existe la restricción de ejecutar un solo comando en una lista de instrucciones.

También es posible usar una variable fuera de un bucle Foreach y aumentar la variable dentro del bucle. En el ejemplo siguiente se cuentan los archivos de un tamaño superior a 100 KB:

          $i = 0
          foreach ($file in Get-ChildItem)
          {
              if ($file.length -gt 100KB) 
              {
                  Write-Host $file "file size:" ($file.length / 
          1024).ToString("F0") KB
                  $i = $i + 1
              }
          }
          if ($i -ne 0)
          {
              Write-Host
              Write-Host $i " file(s) over 100 KB in the current 
          directory."}
          else 
          {
              Write-Host "No files greater than 100 KB in the current 
          directory."
          }

En el ejemplo anterior, la variable $i se establece en 0 fuera del bucle, mientras que la variable se aumenta dentro del bucle para todos los archivos de más de 100 KB que se encuentren. Cuando se sale del bucle, la instrucción If evalúa el valor de $i para mostrar un recuento de todos los archivos de más de 100 KB. También puede mostrar un mensaje que indica que no se encontró ningún archivo de más de 100 KB.

El ejemplo anterior también muestra cómo dar formato a los resultados de la longitud de archivo:

          ($file.length / 1024).ToString("F0")

El valor se divide por 1.024 para mostrar los resultados en kilobytes en lugar de bytes y, a continuación, se da formato al valor resultante mediante el especificador de formato de punto fijo para quitar los valores decimales del resultado. El 0 hace que el especificador de formato no muestre ninguna posición decimal.

La instrucción Foreach dentro de una canalización de comandos

Cuando Foreach aparece en una canalización de comandos, Windows PowerShell usa el alias foreach, que llama al comando ForEach-Object. Cuando se usa el alias foreach en una canalización de comandos, no se incluye la sintaxis ($<item> in $<collection>), tal como se hace con la instrucción Foreach. Esto se debe a que el comando anterior de la canalización proporciona esta información. Cuando se usa en una canalización de comandos, la sintaxis del alias foreach es la siguiente:

          <command> | foreach {<command_block>}

Por ejemplo, el bucle Foreach del comando siguiente muestra los procesos cuyo espacio de trabajo (uso de memoria) es mayor que 20 megabytes (MB).

El comando Get-Process obtiene todos los procesos en el equipo. El alias Foreach ejecuta los comandos del bloque de script en todos los procesos en orden.

La instrucción If selecciona procesos con un espacio de trabajo (WS) mayor que 20 megabytes. El cmdlet Write-Host escribe el nombre del proceso seguido de dos puntos. Divide el valor del espacio de trabajo, que se almacena en bytes, por 1 megabyte para obtener el valor del espacio de trabajo en megabytes. A continuación, convierte el resultado de doble a cadena. Muestra el valor como un número de punto fijo sin decimales (F0), con un separador de espacio (" ") y, después, "MB".

          Write-Host "Processes with working sets greater than 20 MB."
          Get-Process | foreach { 
             if ($_.WS -gt 20MB)
             { Write-Host $_.name ": " ($_.WS/1MB).ToString("F0") MB -Separator ""}
          }

El alias foreach también es compatible con bloques de comandos iniciales, bloques de comandos intermedios y bloques de comandos finales. Los bloques de comandos inicial y final se ejecutan una vez, mientras que el bloque de comandos intermedio se ejecuta cada vez que el bucle Foreach recorre paso a paso una colección o matriz.

A continuación se muestra la sintaxis del alias foreach cuando se usa en una canalización de comandos con un conjunto inicial, intermedio y final de bloques de comandos:

          <command> | foreach {<beginning command_block>}{<middle 
          command_block>}{<ending command_block>}

En el ejemplo siguiente se muestra el uso de los bloques de comandos iniciales, intermedios y finales.

          Get-ChildItem | foreach {
          $fileCount = $directoryCount = 0}{
          if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}{
          "$directoryCount directories and $fileCount files"}

El bloque inicial crea e inicializa dos variables en 0:

          {$fileCount = $directoryCount = 0}

El bloque intermedio evalúa si cada elemento devuelto por Get-ChildItem es un directorio o un archivo:

          {if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}

Si el elemento que se devuelve es un directorio, la variable $directoryCount se incrementa en 1. Si el elemento no es un directorio, la variable $fileCount se incrementa en 1. El bloque final se ejecuta cuando el bloque intermedio completa la operación de bucle y, a continuación, devuelve los resultados de la operación:

          {"$directoryCount directories and $fileCount files"}

Si usa la estructura del bloque de comandos inicial, intermedio y final y el operador de canalización, puede volver a escribir el ejemplo anterior para buscar los archivos mayores de 100 KB, tal como se muestra a continuación:

          Get-ChildItem | foreach{
              $i = 0}{
              if ($_.length -gt 100KB)
              {
                  Write-Host $_.name "file size:" ($_.length / 
          1024).ToString("F0") KB
                  $i++
              }
              }{
              if ($i -ne 0)
              {
                  Write-Host
                  Write-Host "$i file(s) over 100 KB in the current 
          directory."
              }
              else 
              {
              Write-Host "No files greater than 100 KB in the current 
          directory."}
              }

En el ejemplo siguiente, en el que una función devuelve las funciones que se usan en scripts y módulos de script, se muestra cómo usar el método MoveNext (que funciona de forma similar a "omitir X" en un bucle For) y la propiedad Current de la variable $foreach dentro de un bloque de script foreach, incluso si hay definiciones de función espaciadas de forma inusual o incoherente que abarcan varias líneas para declarar el nombre de función. El ejemplo también funciona si hay comentarios en las funciones usadas en un script o módulo de script.

         function Get-FunctionPosition {
             [CmdletBinding()]
             [OutputType('FunctionPosition')]
             param(
                 [Parameter(Position=0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
                 [ValidateNotNullOrEmpty()]
                 [Alias('PSPath')]
                 [System.String[]]
                 $Path
             )
             process {
                 try {
                     $filesToProcess = if ($_ -is [System.IO.FileSystemInfo]) {
                         $_
                     } else {
                         Get-Item -Path $Path
                     }
                     foreach ($item in $filesToProcess) {
                         if ($item.PSIsContainer -or $item.Extension -notin @('.ps1','.psm1')) {
                             continue
                         }
                         $tokens = $errors = $null
                         $ast = [System.Management.Automation.Language.Parser]::ParseFile($item.FullName,([REF]$tokens),([REF]$errors))
                         if ($errors) {
                             Write-Warning "File '$($item.FullName)' has $($errors.Count) parser errors."
                         }
                         :tokenLoop foreach ($token in $tokens) {
                             if ($token.Kind -ne 'Function') {
                                 continue
                             }
                             $position = $token.Extent.StartLineNumber
                             do {
                                 if (-not $foreach.MoveNext()) {
                                     break tokenLoop
                                 }
                                 $token = $foreach.Current
                             } until ($token.Kind -in @('Generic','Identifier'))
                             $functionPosition = [pscustomobject]@{
                                       Name = $token.Text
                                 LineNumber = $position
                                       Path = $item.FullName
                             }
                             Add-Member -InputObject $functionPosition -TypeName FunctionPosition -PassThru
                         }
                     }
                 } catch {
                     throw
                 }
             }
         }

VEA TAMBIÉN

about_Automatic_Variables

about_If

Foreach-Object