Description

Cmdlet ForEach-Object 會對輸入物件集合中的每個專案執行作業。 輸入物件可以使用管線傳送至 Cmdlet,或使用 InputObject 參數指定。

從 Windows PowerShell 3.0 開始,有兩種不同的方法來建構 ForEach-Object 命令。

  • 腳本區塊 。 您可以使用指令碼區塊來指定操作。 在腳本區塊內 $_ ,使用 變數來表示目前的 物件。 指令碼區塊是 Process 參數的值。 腳本區塊可以包含任何 PowerShell 腳本。

    例如,下列命令會取得電腦上每個處理程序之 ProcessName 屬性的值。

    Get-Process | ForEach-Object {$_.ProcessName}

    ForEach-Object 支援 begin process end 區塊,如 about_functions 中所述。

    腳本區塊會在呼叫端的範圍中執行。 因此,區塊可以存取該範圍內的變數,而且可以在 Cmdlet 完成之後,建立保存在該範圍中的新變數。

  • Operation 語句 。 您也可以撰寫作業語句,這更像是自然語言。 您可以使用操作陳述式來指定屬性值或呼叫方法。 操作陳述式是在 Windows PowerShell 3.0 中導入。

    例如,下列命令會一併取得電腦上每個處理程序之 ProcessName 屬性的值。

    Get-Process | ForEach-Object ProcessName

  • 平行執行的腳本區塊 。 從 PowerShell 7.0 開始,可以使用第三個參數集平行執行每個腳本區塊。 ThrottleLimit 參數會限制一次執行的平行腳本數目。 如同之前,使用 $_ 變數來表示腳本區塊中的目前輸入物件。 $using: 使用 關鍵字將變數參考傳遞至執行中的腳本。

    在 PowerShell 7 中,會為每個迴圈反復專案建立新的 Runspace,以確保最大隔離。 相較于建立新的 Runspace,或有許多反復專案執行重大工作,當您執行的工作很小時,這會是大型的效能和資源命中。 從 PowerShell 7.1 開始,預設會重複使用 Runspace 集區中的 Runspace。 Runspace 集區大小是由 ThrottleLimit 參數所指定。 預設 Runspace 集區大小為 5。 您仍然可以使用 UseNewRunspace 參數為每個反復專案建立新的 Runspace。

    根據預設,平行 scriptblocks 會使用啟動平行工作之呼叫端的目前工作目錄。

    如需詳細資訊,請參閱本文的 NOTES 一節。

    Get-ChildItem $PSHOME |
      ForEach-Object -Process {if (!$_.PSIsContainer) {$_.Name; $_.Length / 1024; " " }}

    如果物件不是目錄,腳本區塊會取得檔案的名稱、將其 Length 屬性的值除以 1024,並將空格 (「 」) 「,將它與下一個專案分開。 Cmdlet 會使用 PSISContainer 屬性來判斷物件是否為目錄。

    範例 3:在最新的系統事件上操作

    此範例會將 1000 個最新的事件從系統事件記錄檔寫入文字檔。 目前的時間會顯示在處理事件之前和之後。

    $Events = Get-EventLog -LogName System -Newest 1000
    $events | ForEach-Object -Begin {Get-Date} -Process {Out-File -FilePath Events.txt -Append -InputObject $_.Message} -End {Get-Date}

    Get-EventLog 會從系統事件記錄檔中取得 1000 個最新的事件,並將其儲存在 變數中 $Events $Events 接著會使用管線傳送至 ForEach-Object Cmdlet。 Begin 參數會顯示目前的日期和時間。 接下來, Process 參數會 Out-File 使用 Cmdlet 來建立名為 events.txt 的文字檔,並將每個事件的訊息屬性儲存在該檔案中。 最後, End 參數用來顯示所有處理完成後的日期和時間。

    範例 4:變更登錄機碼的值

    本範例會將機碼下 HKCU:\Network 所有子機碼中的 RemotePath 登錄專案值變更為大寫文字。

    Get-ItemProperty -Path HKCU:\Network\* |
      ForEach-Object {Set-ItemProperty -Path $_.PSPath -Name RemotePath -Value $_.RemotePath.ToUpper();}

    您可以使用這個格式來變更登錄項目值的形式或內容。

    網路 金鑰中的每個子機碼都代表在登入時重新連線的對應網路磁碟機機。 RemotePath 項目包含連線的磁碟機的 UNC 路徑。 例如,如果您將 E: 磁片磁碟機對應至 \\Server\Share ,則會在 中 HKCU:\Network 建立 E 子機碼,並將 RemotePath 登錄值設定為 \\Server\Share

    此命令會 Get-ItemProperty 使用 Cmdlet 來取得 網路 機碼的所有子機碼和 Set-ItemProperty Cmdlet,以變更每個機碼中的 RemotePath 登錄專案值。 在 Set-ItemProperty 命令中,路徑是登錄機碼 的 PSPath 屬性值。 這是代表登錄機碼的 Microsoft .NET Framework 物件屬性,而不是登錄專案。 此命令會使用 RemotePath 值的 ToUpper () 方法,這是字串 (REG_SZ) 。

    因為 Set-ItemProperty 正在變更每個索引鍵的 屬性,所以 ForEach-Object 需要 Cmdlet 才能存取屬性。

    範例 5:使用$null自動變數

    此範例顯示將自動變數管線傳送 $null ForEach-Object Cmdlet 的效果。

    1, 2, $null, 4 | ForEach-Object {"Hello"}
    Hello
    Hello
    Hello
    Hello

    因為 PowerShell 會將 $null 視為明確的預留位置,所以 ForEach-Object Cmdlet 會產生 的值 $null ,就像對透過管道傳送給它的其他物件所做的一樣。

    範例 6:取得屬性值

    此範例會使用 Cmdlet 的 MemberName 參數,取得所有已安裝 PowerShell 模組的 ForEach-Object Path 屬性值。

    Get-Module -ListAvailable | ForEach-Object -MemberName Path
    Get-Module -ListAvailable | Foreach Path

    第二個命令同等於第一個命令。 它會使用 Foreach Cmdlet 的 ForEach-Object 別名,並省略 MemberName 參數的名稱,這是選擇性的。

    Cmdlet ForEach-Object 對於取得屬性值很有用,因為它會取得值而不變更類型,不同于 Format Cmdlet 或 Select-Object Cmdlet,這會變更屬性值類型。

    範例 7:將模組名稱分割成元件名稱

    此範例示範將兩個點分隔模組名稱分割成其元件名稱的三種方式。 命令呼叫字串的 Split 方法。 這三個命令使用不同的語法,但它們是相等的並且可交換使用。 這三種情況的輸出都相同。

    "Microsoft.PowerShell.Core", "Microsoft.PowerShell.Host" | ForEach-Object {$_.Split(".")}
    "Microsoft.PowerShell.Core", "Microsoft.PowerShell.Host" | ForEach-Object -MemberName Split -ArgumentList "."
    "Microsoft.PowerShell.Core", "Microsoft.PowerShell.Host" | Foreach Split "."
    Microsoft
    PowerShell
    Microsoft
    PowerShell
    	

    第一個命令會使用傳統的語法,其中包括腳本區塊和目前的物件運算子 $_ 。 它使用點語法來指定要括住分隔符號引數的方法和括號。

    第二個命令會使用 MemberName 參數來指定 Split 方法和 ArgumentList 參數,將點 (.) 識別為分割分隔符號。

    第三個命令會使用 Cmdlet 的 ForEach-ObjectForeach別名,並省略MemberNameArgumentList參數的名稱,這是選擇性的。

    範例 8:搭配兩個腳本區塊使用 ForEach-Object

    在此範例中,我們會以位置傳遞兩個腳本區塊。 所有腳本區塊都會系結至 Process 參數。 不過,它們會被視為已傳遞至 BeginProcess 參數。

    1..2 | ForEach-Object { 'begin' } { 'process' }
    begin
    process
    process

    範例 9:搭配兩個以上的腳本區塊使用ForEach-Object

    在此範例中,我們會以位置方式傳遞四個腳本區塊。 所有腳本區塊都會系結至 Process 參數。 不過,它們會被視為已傳遞至 BeginProcessEnd 參數。

    1..2 | ForEach-Object { 'begin' } { 'process A' }  { 'process B' }  { 'end' }
    begin
    process A
    process B
    process A
    process B
    

    第一個腳本區塊一律會對應至 begin 區塊,最後一個區塊會對應至 end 區塊,而 之間的區塊全都對應至 process 區塊。

    範例 10:針對每個管線專案執行多個腳本區塊

    如先前範例所示,使用 Process 參數傳遞的多個腳本區塊會對應至 BeginEnd 參數。 若要避免此對應,您必須提供 BeginEnd 參數的明確值。

    1..2 | ForEach-Object -Begin $null -Process { 'one' }, { 'two' }, { 'three' } -End $null
    three
    three

    範例 11:以平行批次執行慢速腳本

    此範例會執行腳本區塊,以評估字串並睡眠一秒。

    $Message = "Output:"
    1..8 | ForEach-Object -Parallel {
        "$using:Message $_"
        Start-Sleep 1
    } -ThrottleLimit 4
    Output: 1
    Output: 2
    Output: 3
    Output: 4
    Output: 5
    Output: 6
    Output: 7
    Output: 8

    ThrottleLimit參數值設定為 4,以便以四個批次處理輸入。 關鍵字 $using: 用來將 $Message 變數傳遞至每個平行腳本區塊。

    範例 12:平行擷取記錄專案

    此範例會從本機 Windows 電腦上的 5 個系統記錄擷取 50,000 個記錄專案。

    $logNames = 'Security','Application','System','Windows PowerShell','Microsoft-Windows-Store/Operational'
    $logEntries = $logNames | ForEach-Object -Parallel {
        Get-WinEvent -LogName $_ -MaxEvents 10000
    } -ThrottleLimit 5
    $logEntries.Count
    50000

    Parallel 參數會針對每個輸入記錄名稱指定平行執行的指令碼區塊。 ThrottleLimit參數可確保所有五個腳本區塊都會同時執行。

    範例 13:以作業方式平行執行

    此範例會建立一個作業,以平行方式執行腳本區塊,一次兩個。

    $job = 1..10 | ForEach-Object -Parallel {
        "Output: $_"
        Start-Sleep 1
    } -ThrottleLimit 2 -AsJob
    $job | Receive-Job -Wait
    Output: 1
    Output: 2
    Output: 3
    Output: 4
    Output: 5
    Output: 6
    Output: 7
    Output: 8
    Output: 9
    Output: 10

    變數 $job 會接收收集輸出資料和監視執行中狀態的工作物件。 工作物件會使用Wait參數透過管線傳送至 Receive-Job ,它會將輸出串流至主控台,就像 ForEach-Object -Parallel 沒有AsJob一樣執行。

    範例 14:使用執行緒安全變數參考

    此範例會平行叫用腳本區塊,以收集唯一命名的 Process 物件。

    $threadSafeDictionary = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new()
    Get-Process | ForEach-Object -Parallel {
        $dict = $using:threadSafeDictionary
        $dict.TryAdd($_.ProcessName, $_)
    $threadSafeDictionary["pwsh"]
    NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
     ------    -----      -----     ------      --  -- -----------
         82    82.87     130.85      15.55    2808   2 pwsh

    ConcurrentDictionary物件的單一實例會傳遞至每個腳本區塊來收集物件。 由於 ConcurrentDictionary 是安全線程,因此每個平行腳本都能夠安全地修改。 非安全線程的物件,例如 System.Collections.Generic.Dictionary,無法放心在此使用。

    這個範例是 平行 參數的非常沒有效率的使用。 腳本只會將輸入物件新增至並行字典物件。 這很簡單,不值得在個別執行緒中叫用每個腳本的額外負荷。 在不使用平行交換器的情況下正常執行 ForEach-Object 會更有效率且更快速。 此範例僅供示範如何使用執行緒安全變數。

    範例 15:使用平行執行撰寫錯誤

    這個範例會以平行方式寫入錯誤資料流程,其中寫入錯誤的順序是隨機的。

    1..3 | ForEach-Object -Parallel {
        Write-Error "Error: $_"
    Write-Error: Error: 1
    Write-Error: Error: 3
    Write-Error: Error: 2

    範例 16:平行執行中終止錯誤

    此範例示範一個平行執行的 scriptblock 中的終止錯誤。

    1..5 | ForEach-Object -Parallel {
        if ($_ -eq 3)
            throw "Terminating Error: $_"
        Write-Output "Output: $_"
    Exception: Terminating Error: 3
    Output: 1
    Output: 4
    Output: 2
    Output: 5

    Output: 3 永遠不會寫入,因為該反復專案的平行腳本區塊已終止。

    即使使用 $using: 關鍵字,在案例中 Foreach-Object -Parallel也不支援PipelineVariable一般參數變數。

    範例 17:在巢狀平行腳本 ScriptBlockSet 中傳遞變數

    您可以在限定範圍的 scriptblock 之外 Foreach-Object -Parallel 建立變數,並在 scriptblock 內搭配 $using 關鍵字使用。

    $test1 = 'TestA'
    1..2 | Foreach-Object -Parallel {
        $using:test1
    TestA
    TestA
    # You CANNOT create a variable inside a scoped scriptblock
    # to be used in a nested foreach parallel scriptblock.
    $test1 = 'TestA'
    1..2 | Foreach-Object -Parallel {
        $using:test1
        $test2 = 'TestB'
        1..2 | Foreach-Object -Parallel {
            $using:test2
    Line |
       2 |  1..2 | Foreach-Object -Parallel {
         |         ~~~~~~~~~~~~~~~~~~~~~~~~~~
         | The value of the using variable '$using:test2' cannot be retrieved because it has not been set in the local session.

    巢狀腳本區塊無法存取 $test2 變數,並擲回錯誤。

    範例 18:建立多個平行執行腳本的作業

    ThrottleLimit 參數會限制在每個 實例 ForEach-Object -Parallel 期間執行的平行腳本數目。 它不會限制使用 AsJob 參數時可建立的作業數目。 由於作業本身會同時執行,因此可以建立一些平行作業,每個作業都會執行到並行腳本區塊的節流限制數目。

    $jobs = for ($i=0; $i -lt 10; $i++) {
        1..10 | ForEach-Object -Parallel {
            ./RunMyScript.ps1
        } -AsJob -ThrottleLimit 5
    $jobs | Receive-Job -Wait

    此範例會建立 10 個執行中的作業。 每個作業不會同時執行 5 個腳本。 同時執行的實例總數限制為 50 (10 個作業, 而 ThrottleLimit 為 5) 。

    -ArgumentList

    指定方法呼叫的引數陣列。 如需 ArgumentList行為的詳細資訊,請參閱 about_Splatting

    此參數是在 Windows PowerShell 3.0 引進。

    Type:Object[] Aliases:Args Position:Named Default value:None Accept pipeline input:False Accept wildcard characters:False

    -AsJob

    讓平行調用以 PowerShell 作業的形式執行。 會傳回單一工作物件,而不是執行中的腳本區塊的輸出。 工作物件包含每個執行之平行腳本區塊的子作業。 所有 PowerShell 作業 Cmdlet 都可以使用此工作物件來監視執行中狀態並擷取資料。

    此參數是在 PowerShell 7.0 中引進的。

    Type:SwitchParameter Position:Named Default value:None Accept pipeline input:False Accept wildcard characters:False

    -Begin

    指定在這個 Cmdlet 處理任何輸入物件之前執行的腳本區塊。 此腳本區塊只會針對整個管線執行一次。 如需 區塊的詳細資訊 begin ,請參閱 about_Functions

    Type:ScriptBlock Position:Named Default value:None Accept pipeline input:False Accept wildcard characters:False

    -Confirm

    在執行 Cmdlet 前提示您確認。

    Type:SwitchParameter Aliases:cf Position:Named Default value:False Accept pipeline input:False Accept wildcard characters:False

    指定在這個 Cmdlet 處理所有輸入物件之後執行的腳本區塊。 此腳本區塊只會針對整個管線執行一次。 如需 區塊的詳細資訊 end ,請參閱 about_Functions

    Type:ScriptBlock Position:Named Default value:None Accept pipeline input:False Accept wildcard characters:False

    -InputObject

    指定輸入物件。 ForEach-Object 在每個輸入物件上執行腳本區塊或 operation 語句。 輸入包含物件的變數,或輸入可取得物件的命令或運算式。

    當您搭配 使用InputObject參數搭配 ForEach-Object 時,會將 InputObject值視為單一物件,而不是將命令結果管線傳送至 ForEach-Object 。 即使值是命令結果的集合,例如 -InputObject (Get-Process) ,也是如此。 由於 InputObject 無法從物件的陣列或集合傳回個別屬性,因此,如果您用來 ForEach-Object 對定義屬性中具有特定值之物件的物件集合執行作業,請在 ForEach-Object 管線中使用,如本主題中的範例所示。

    Type:PSObject Position:Named Default value:None Accept pipeline input:True Accept wildcard characters:False

    -MemberName

    指定要取得的屬性或要呼叫的方法。

    允許萬用字元,但只有在產生的字串解析為唯一值時,才能運作。 例如,如果您執行 Get-Process | ForEach -MemberName *Name ,萬用字元模式會比對一個以上的成員,導致命令失敗。

    此參數是在 Windows PowerShell 3.0 引進。

    Type:String Position:0 Default value:None Accept pipeline input:False Accept wildcard characters:True

    -Parallel

    指定要用於平行處理輸入物件的腳本區塊。 輸入描述操作的指令碼區塊。

    此參數是在 PowerShell 7.0 中引進的。

    Type:ScriptBlock Position:Named Default value:None Accept pipeline input:False Accept wildcard characters:False

    -Process

    指定在每個輸入物件上執行的操作。 此腳本區塊會針對管線中的每個物件執行。 如需 區塊的詳細資訊 process ,請參閱 about_Functions

    當您將多個腳本區塊提供給 Process 參數時,第一個腳本區塊一律會對應至 begin 區塊。 如果只有兩個腳本區塊,則第二個區塊會對應至 process 區塊。 如果有三個以上的腳本區塊,則第一個腳本區塊一律會對應至 begin 區塊,最後一個區塊會對應至 end 區塊,而兩者之間的區塊全都對應至 process 區塊。

    Type:ScriptBlock[] Position:0 Default value:None Accept pipeline input:False Accept wildcard characters:False

    -RemainingScripts

    指定 Process 參數未採用的所有腳本區塊。

    此參數是在 Windows PowerShell 3.0 引進。

    Type:ScriptBlock[] Position:Named Default value:None Accept pipeline input:False Accept wildcard characters:False

    -ThrottleLimit

    指定平行的腳本區塊數目。 輸入物件會遭到封鎖,直到執行中的腳本區塊計數低於 ThrottleLimit為止。 預設值是 5

    ThrottleLimit 參數會限制在每個 實例 ForEach-Object -Parallel 期間執行的平行腳本數目。 它不會限制使用 AsJob 參數時可建立的作業數目。 由於作業本身會同時執行,因此可以建立一些平行作業,每個作業都會執行到並行腳本區塊的節流限制數目。

    此參數是在 PowerShell 7.0 中引進的。

    Type:Int32 Position:Named Default value:5 Accept pipeline input:False Accept wildcard characters:False

    -TimeoutSeconds

    指定等候所有輸入平行處理的秒數。 在指定的逾時時間之後,所有執行中的腳本都會停止。 而且會忽略要處理的任何其餘輸入物件。 的 0 預設值會停用逾時,而且 ForEach-Object -Parallel 可以無限期執行。 在命令列中輸入Ctrl+C會停止執行 ForEach-Object -Parallel 中的命令。 此參數不能與 AsJob 參數一起使用。

    此參數是在 PowerShell 7.0 中引進的。

    Type:Int32 Position:Named Default value:0 Accept pipeline input:False Accept wildcard characters:False

    -UseNewRunspace

    讓平行調用為每個迴圈反復專案建立新的 Runspace,而不是從 Runspace 集區重複使用 Runspace。

    此參數是在 PowerShell 7.1 中引進的

    Type:SwitchParameter Position:Named Default value:False Accept pipeline input:False Accept wildcard characters:False

    -WhatIf

    顯示執行 Cmdlet 後會發生的情況。 Cmdlet 並不會執行。

    Type:SwitchParameter Aliases:wi Position:Named Default value:False Accept pipeline input:False Accept wildcard characters:False

    PSObject

    您可以使用管線將任何物件傳送至此 Cmdlet。

    PSObject

    此 Cmdlet 會傳回輸入所決定的物件。

    PowerShell 包含下列的 ForEach-Object 別名:

  • 所有平臺:
    • foreach
    • Cmdlet ForEach-Object 的運作方式與 Foreach 語句非常類似,不同之處在于您無法使用管線將輸入傳送至 Foreach 語句。 如需 Foreach 語句的詳細資訊,請參閱 about_Foreach

      從 PowerShell 4.0 開始, WhereForEach 新增方法以搭配集合使用。 您可以在這裡深入瞭解這些新方法 about_arrays

      使用 ForEach-Object -Parallel

    • 參數 ForEach-Object -Parallel 集會使用 PowerShell 的內部 API,在新 Runspace 中執行每個腳本區塊。 相較于以循序處理正常執行,這明顯比執行 ForEach-Object 額外負荷還要多。 相較于腳本區塊執行的工作,平行執行的額外負荷很小,請務必使用 Parallel 。 例如:

    • 在多核心電腦上計算密集腳本
    • 花費時間等候結果或執行檔案作業的腳本
    • 使用 Parallel 參數可能會導致腳本執行速度比正常慢很多。 特別是平行腳本很簡單時。 使用 Parallel 進行實驗,以探索其可能有説明的位置。

    • 以平行方式執行時,如果以 ScriptPropertiesScriptMethods 裝飾的物件是在與腳本原本附加至不同的 Runspace 中執行,則無法保證能夠正常運作。

      無論實際叫用的位置為何,Scriptblock 調用一律會嘗試在其 Runspace 中執行。 不過, ForEach-Object -Parallel 會建立在使用後刪除的暫存 Runspace,因此腳本不會再執行 Runspace。

      只要 Runspace 仍然存在,此行為就可以運作。 不過,如果腳本相依于只存在於呼叫端 Runspace 中的外部變數,而不是 Runspace,則可能無法取得所需的結果。

    • 非終止錯誤會寫入 Cmdlet 錯誤資料流程,因為它們會在平行執行的 scriptblock 中發生。 因為平行 scriptblock 執行順序不具決定性,所以錯誤串流中出現錯誤的順序是隨機的。 同樣地,寫入至其他資料流程的訊息,例如警告、詳細資訊或資訊會以不確定的順序寫入這些資料流程。

      終止錯誤,例如例外狀況,會終止其發生之 scriptblock 的個別平行實例。 一個 scriptblock 中的終止錯誤可能不會造成 Cmdlet 的 Foreach-Object 終止。 其他腳本區塊會以平行方式執行,除非它們也遇到終止錯誤,否則會繼續執行。 終止錯誤會以具有的 FullyQualifiedErrorIdPSTaskException 做為ErrorRecord寫入錯誤資料流程。 終止錯誤可以使用 PowerShell try/catchtrap 區塊轉換成非終止錯誤。

    • 即使使用 $using: 關鍵字,在平行案例中也不支援PipelineVariable通用參數變數。

      參數集會在 ForEach-Object -Parallel 不同的進程執行緒上平行執行腳本區塊。 $using:關鍵字允許將變數參考從 Cmdlet 調用執行緒傳遞至每個執行中的腳本區塊執行緒。 由於腳本區塊在不同的執行緒中執行,因此必須安全地使用以傳址方式傳遞的物件變數。 一般而言,從未變更的參考物件讀取是安全的。 但是,如果要修改物件狀態,則必須使用執行緒安全物件,例如 .NET System.Collection.Concurrent 類型 (請參閱範例 11) 。

  •