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