共用方式為


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 的參數。第一個命令會傳回相同的結果但不傳回錯誤,因為簡化的語法不會嘗試為沒有套用指定引數的項目傳回結果。

在此範例中,Get-Process 屬性描述被視為 ForEach 陳述式的參數引數而略過。所得結果是作用中處理序的描述。

          Get-Process | ForEach Description

命令管線外的 Foreach 陳述式

Foreach 陳述式被括弧括住的部分,表示要逐一執行的變數和集合。當 Foreach 迴圈執行時,Windows PowerShell 會自動建立變數 ($<項目>)。變數在逐一執行迴圈之前,會設為集合中的值。Foreach 陳述式 {<statement list>} 之後的區塊包含一組針對集合中各項目執行的命令。

範例

例如,下列範例中的 Foreach 迴圈會顯示 $letterArray 陣列中的值。

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

此範例利用字串值 "a"、"b"、"c" 以及 "d" 建立並初始化 $letterArray 陣列。Foreach 陳述式第一次執行時,會將 $letter 變數設定為等於 $letterArray 中的第一個項目 ("a")。然後,其會使用 Write-Host Cmdlet 來顯示字母 a。下一次通過迴圈時,$letter 則會設為 "b",依此類推。在 Foreach 迴圈顯示字母 d 之後,Windows PowerShell 會結束迴圈。

整個 Foreach 陳述式必須出現在同一行,以命令的方式在 Windows PowerShell 命令提示字元執行。如果您將命令改為放在 .ps1 指令碼檔案中,則整個 Foreach 陳述式就不需出現在同一行。

Foreach 陳述式也可與會傳回項目集合的 Cmdlet 搭配使用。在下列範例中,Foreach 陳述式會逐步處理 Get-Childitem Cmdlet 所傳回的清單項目。

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

您可以使用 If 陳述式來限制傳回的結果,藉以精簡此範例。在下列範例中,Foreach 陳述式會執行與上述範例相同的迴圈作業,但其增加了 If 陳述式來將結果限制為大小超過 100 KB 的檔案:

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

在此範例中,Foreach 迴圈會使用 $file 變數的屬性執行比較運算 ($file.length -gt 100KB)。$file 變數包含 Get-ChildItem Cmdlet 所傳回物件的所有屬性。因此,您還能傳回檔名以外的其他資訊。在下一個範例中,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 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."
          }

在前面的範例中,$i 變數在迴圈外設為 0,而此變數在迴圈內,每找到一個大於 100 KB 的檔案即隨之遞增。當迴圈結束時,If 陳述式會評估 $i 的值來顯示所有超過 100 KB 的檔案計數。或者會顯示訊息表示沒有超過 100 KB 的檔案。

前一個範例也示範了如何格式化檔案長度的結果:

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

該值會除以 1024,以 KB 而非位元組為單位顯示結果,並使用固定點格式指定元來移除結果中的任何小數值,將結果值格式化。0 會讓格式指定元不顯示任何小數位數。

在命令管線中的 Foreach 陳述式

當 Foreach 出現在命令管線中時,Windows PowerShell 會使用 foreach 別名呼叫 ForEach-Object 命令。當您在命令管線中使用 foreach 別名時,不需包含 Foreach 陳述式中的 ($<集合> 中的 $<項目>) 語法。這是因為管線中先前的命令提供了這項資訊。用於命令管線的 foreach 別名語法如下所示:

          <command> | foreach {<command_block>}

例如,下列命令中的 Foreach 迴圈會顯示工作集 (記憶體使用量) 大於 20 MB 的處理序。

Get-Process 命令會取得電腦上的所有處理程序。Foreach 別名會依序分別執行每個處理序上指令碼區塊中的命令。

IF 陳述式會選取工作集 (WS) 大於 20 MB 的處理序。Write-Host Cmdlet 會寫入冒號後面的處理序名稱。其會將以位元組為單位儲存的工作集值,除以 1 MB 來獲得以 MB 為單位的工作集值。然後其會將結果從雙精度浮點數轉換為字串。其會將值顯示為零小數位數 (F0) 的定點數字、一個空格分隔符號 (「 」),然後是 "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 ""}
          }

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。如果項目不是目錄,$fileCount 變數就會遞增 1。結束區塊會在中間區塊完成其迴圈作業後執行,然後再傳回作業結果:

          {"$directoryCount directories and $fileCount files"}

藉由使用起始、中間和結束命令區塊結構及管線運算子,您可以重新撰寫先前的範例來尋找任何大於 100 KB 的檔案,如下所示:

          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 方法 (其運作方式類似 For 迴圈上的 "skip X") 和 Foreach 指令碼區塊內 $foreach 變數的 Current 屬性,即使有異常或不一致的間距函式定義跨越多行來宣告函式名稱。此範例在指令碼或指令碼模組使用函式的註解時也適用。

         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