Поделиться через


about_ForEach

Назначение: Windows PowerShell 2.0, Windows PowerShell 3.0

РАЗДЕЛ

about_Foreach

КРАТКОЕ ОПИСАНИЕ

Содержит описание команды языка, позволяющей перебирать все элементы в коллекции.

ПОДРОБНОЕ ОПИСАНИЕ

Оператор Foreach (называемый также циклом Foreach) является языковой конструкцией для пошагового перемещения (итерации) по последовательности значений в коллекции элементов.

Самым простым и наиболее часто используемым типом коллекции, по которой производится перемещение, является массив. Обычно в цикле Foreach одна или несколько команд выполняются применительно к каждому элементу массива.

Синтаксис

Ниже показан синтаксис оператора ForEach.

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

Упрощенный синтаксис

Начиная с версии Windows PowerShell® 3.0 синтаксис с ключевыми словами языка, например Where и ForEach, был упрощен. Операторы сравнения, которые применяются к элементам коллекции, рассматриваются как параметры. К элементам коллекции можно применять метод, не включая его в блок сценария и не добавляя автоматическую переменную «$_.». Рассмотрим следующие два примера:

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

Хотя обе команды работают, первая возвращает результаты без использования блока сценария или автоматической переменной «$_.». Метод GetKeyAlgorithm рассматривается как параметр оператора ForEach. Первая команда возвращает те же результаты, но без ошибок, так как упрощенный синтаксис не пытается вернуть результаты для элементов, к которым не был применен указанный аргумент.

В этом примере свойство Description командлета Get-Process передается в качестве аргумента параметра оператора ForEach. Результаты представляют собой описания активных процессов.

          Get-Process | ForEach Description

Оператор Foreach за пределами конвейера команд

В заключаемой в круглые скобки части оператора Foreach указываются переменная и коллекция для перебора. При выполнении цикла Foreach среда Windows PowerShell автоматически создает переменную ($<элемент>). Перед каждой итерацией в цикле переменной присваивается значение в коллекции. Блок после оператора Foreach {<список_операторов>} содержит набор команд, выполняемых применительно к каждому элементу коллекции.

Примеры

Например, цикл Foreach в приведенном ниже примере отображает значения в массиве $letterArray.

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

В этом примере создается массив $letterArray, который затем инициализируется строковыми значениями «a», «b», «c» и «d». При первом выполнении оператора Foreach переменная $letter устанавливается равной первому элементу в массиве $letterArray («a»). Затем буква «a» отображается с помощью командлета Write-Host. При следующей итерации цикла переменной $letter присваивается значение «b» и т. д. После того как цикл Foreach отобразит букву «d», Windows PowerShell выходит из цикла.

Чтобы оператор Foreach выполнялся как команда в командной строке Windows PowerShell, он должен быть представлен в одной строке целиком. Оператор Foreach не обязательно должен быть представлен в одной строке целиком, если команда помещена в файл сценария PS1.

Операторы Foreach могут также использоваться вместе с командлетами, возвращающими коллекции элементов. В приведенном ниже примере оператор Foreach выполняет перебор элементов списка, возвращаемого командлетом Get-ChildItem.

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

Пример можно усовершенствовать с помощью оператора If для ограничения возвращаемых результатов. В примере ниже оператор Foreach выполняет те же операции в цикле, что и в предыдущем примере, но здесь добавлена инструкция If, ограничивающая результаты файлами, размер которых превышает 100 килобайт (КБ).

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

В этом примере цикл Foreach использует свойство переменной $file для выполнения операции сравнения ($file.length -gt 100KB). Переменная $file содержит все свойства в объекте, возвращаемом командлетом Get-ChildItem. Поэтому может возвращаться не только имя файла. В следующем примере Windows PowerShell внутри списка операторов возвращает длину и время последнего обращения.

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

В этом примере в списке операторов не обязательно выполнять только одну команду.

Кроме того, можно использовать переменную вне цикла Foreach и увеличивать ее значение внутри цикла. В примере ниже подсчитываются файлы, размер которых превышает 100 КБ.

          $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."
          }

В предыдущем примере переменной $i присваивается значение 0 вне цикла, а увеличение значения происходит внутри цикла для каждого найденного файла размером больше 100 КБ. При выходе из цикла инструкция If вычисляет значение переменной $i для отображения количества всех файлов размером более 100 КБ. Либо выводится сообщение о том, что ни одного файла размером более 100 КБ не найдено.

Предыдущий пример также демонстрирует, как форматировать результаты для длины.

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

Значение делится на 1024, чтобы показать результаты в килобайтах, а не в байтах. Полученное значение далее форматируется с помощью спецификатора формата с фиксированной запятой для удаления из результата дробной части. Значение 0 спецификатора формата указывает, что дробная часть отображаться не должна.

Оператор Foreach внутри конвейера команд

Когда оператор Foreach используется в конвейере команд, Windows PowerShell использует псевдоним foreach, вызывающий команду ForEach-Object. При использовании псевдонима foreach в конвейере команд не используется синтаксическая конструкция ($<элемент> in $<коллекция>), как это делается с оператором Foreach. Это является следствием того, что эти сведения предоставляет предыдущая команда в конвейере. Синтаксис псевдонима foreach, применяемого в конвейере команд, выглядит следующим образом:

          <command> | foreach {<command_block>}

Например, цикл Foreach в следующем конвейере команд отображает процессы, рабочий набор (использование памяти) которых превышает 20 мегабайт (МБ).

Команда Get-Process получает имена всех процессов на компьютере. Псевдоним Foreach выполняет команды в блоке сценария для каждого процесса в последовательности.

Оператор IF выбирает процессы с рабочим набором (WS) больше 20 мегабайт. Командлет Write-Host записывает имя процесса и ставит после него двоеточие. Он делит значение рабочего набора, которое хранится в байтах, на 1 мегабайт, чтобы получить значение рабочего набора в мегабайтах. Затем он преобразует результат из типа double в строку. Значение выводится в виде числа с фиксированной запятой с нулем десятичных знаков (F0), за которым следует пробел (« ») и «МБ».

          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 ""}
          }

Псевдоним foreach также поддерживает начальные, средние и конечные блоки команд. Начальный и конечный блоки команд выполняются один раз, а средний блок команд выполняется каждый раз, когда цикл Foreach перемещается к очередному элементу коллекции или массива.

Синтаксис псевдонима foreach, используемого в конвейере команд с начальным, средним и конечным блоками команд, выглядит следующим образом:

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

В примере ниже демонстрируется использование начального, среднего и конечного блоков команд.

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

Начальный блок создает и инициализирует две переменные со значением 0.

          {$fileCount = $directoryCount = 0}

Средний блок проверяет, является ли возвращаемый командлетом Get-ChildItem элемент каталогом или файлом.

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

Если возвращаемый элемент является каталогом, значение переменной $directoryCount увеличивается на 1. Если элемент не является каталогом, на 1 увеличивается значение переменной $fileCount. Конечный блок выполняется после того, как средний блок завершает цикл и возвращает результат операции.

          {"$directoryCount directories and $fileCount files"}

С помощью структуры начального, среднего и конечного блоков команд и оператора конвейера можно переписать приведенный выше пример поиска файлов размером более 100 КБ следующим образом:

          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."}
              }

В приведенном ниже примере функции, которая возвращает функции, использующиеся в сценариях и модулях сценариев, демонстрируется, как использовать метод MoveNext (работающий аналогично «skip X» в цикле For) и свойство Current переменной $foreach в блоке сценария foreach даже при наличии необычных или неединообразных определений функций, имена которых объявлены в нескольких строках. Этот пример также работает при наличии комментариев в функциях, используемых в сценарии или модуле сценария.

         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
                 }
             }
         }

СМ. ТАКЖЕ

about_Automatic_Variables

about_If

Foreach-Object