about_Pipelines

適用於: Windows PowerShell 2.0, Windows PowerShell 3.0, Windows PowerShell 4.0, Windows PowerShell 5.0

主題

about_pipelines

簡短描述

結合命令到 Windows PowerShell 中的管線

詳細描述

管線是管線運算子 (|) (ASCII 124) 所連線的一系列命令。每個管線運算子都會將上一個命令的結果傳送至下一個命令。

您可以使用管線來傳送命令所輸出的物件,用來當做另一個命令的輸入,以便處理。此外,您可以將該命令的輸出傳送到另一個命令。最後可產生功能非常強大的命令鏈結,也就是「管線」,由一系列簡單的命令所組成。

例如,

Command-1 | Command-2 | Command-3  

在此範例中,Command-1 發出的物件都會傳送至 Command-2。Command-2 處理物件,並將它們傳送到 Command-3。Command-3 處理物件,並將它們順著管線向下傳遞。因為在該管線中有沒有其他命令,所以結果會顯示在主控台上。

在管線中,會以命令出現的順序從左到右處理命令。這種處理程序可做為單一作業處理,且會在產生時顯示輸出。

以下是簡單的範例。下列命令會取得記事本程序,然後將其停止。

         get-process notepad | stop-process

第一個命令使用 Get-Process Cmdlet 來取得代表記事本處理程序的物件。它會使用管線運算子 (|) 將處理程序物件傳送給 Stop-Process Cmdlet,這會停止記事本處理程序。請注意 Stop-Process 命令沒有 Name 或 ID 參數來指定處理程序,因為指定的處理程序已透過管線提交。

以下是一個實用的範例。此命令管線取得目前目錄中的文字檔案,並只會選取超過 10000 個位元組長的檔案,依長度排序,然後在資料表中顯示每個檔案的名稱和長度。

        Get-ChildItem -path *.txt | Where-Object {$_.length -gt 10000} | 
        Sort-Object -property Length | Format-Table -property name, length

此管線由指定之順序的四個命令所組成。此命令會水平寫入,但是我們將在下圖中以垂直方式示範此程序。

       Get-ChildItem -path *.txt

                  |
                  |   (FileInfo objects )
                  |   (    .txt         )
                  |
                  V                   

       Where-Object {$_.length -gt 10000}

                  |
                  |   (FileInfo objects )
                  |   (    .txt         )
                  |   ( Length > 10000  )
                  |
                  V

       Sort-Object -property Length

                  |
                  |   (FileInfo objects  )
                  |   (    .txt          )
                  |   ( Length > 10000   )
                  |   ( Sorted by length )
                  |
                  V

       Format-Table -property name, length

                  |   
                  |   (FileInfo objects     )
                  |   (    .txt             )
                  |   ( Length > 10000      )
                  |   ( Sorted by length    )
                  |   (Formatted in a table )
                  |
                  V
        Name                       Length
        ----                       ------
        tmp1.txt                    82920
        tmp2.txt                   114000
        tmp3.txt                   114000

使用管線

Windows PowerShell Cmdlet 設計目的是要在管線中使用。例如,您通常可將 Get Cmdlet 的結果輸送到相同名詞的動作 Cmdlet (例如 Set、Start、Stop 或 Rename Cmdlet)。

例如,您可以將 Get-Service Cmdlet 的任何服務輸送到 Start-Service 或 Stop-Service Cmdlet (雖然停用的服務無法以這種方式重新啟動)。

此命令管線會在電腦上啟動 WMI 服務:

get-service wmi | start-service

取得和設定 Windows PowerShell 提供者物件的 Cmdlet,例如 Item 和 ItemProperty Cmdlet,其設計目的也是在管線中使用。

例如,您可以將 Windows PowerShell 登錄提供者中 Get-Item 或 Get-ChildItem 命令的結果輸送到 New-ItemProperty Cmdlet。此命令會將新的登錄項目 NoOfEmployees 與其值 8124,加入 MyCompany 登錄機碼。

       get-item -path HKLM:\Software\MyCompany | new-Itemproperty -name NoOfEmployees -value 8124

許多公用程式 Cmdlet,例如 Get-Member、Where-Object、Sort-Object、Group-Object 和 Measure-Object 幾乎是管線專用的。您可以輸送任何物件至這些 Cmdlet。

例如,您可以將電腦上所有處理程序輸送到 Sort-Object 命令,然後讓它們依照處理程序中控制代碼的數目排序。

get-process | sort-object -property handles

此外,您可以輸送任何物件到格式化 Cmdlet,例如 Format-List 和 Format-Table、Export Cmdlet,例如 Export-Clixml 和 Export-CSV,以及 Out Cmdlet,例如 Out-Printer。

例如,您可以將 Winlogon 處理程序輸送到 Format-List Cmdlet,藉此在清單中顯示該處理程序的所有屬性。

get-process winlogon | format-list -property *

稍加練習後,您會發現將簡單的命令結合成管線可以節省時間與輸入量,可讓您的指令碼更有效率。

管線的運作方式

當您「輸送」物件時,也就是將某個命令的輸出物件傳送到另一個命令時,Windows PowerShell 會嘗試將輸送的物件與接收到的 Cmdlet 參數之一建立關聯。

為了這樣做,Windows PowerShell「參數繫結」元件會將輸入物件與 Cmdlet 參數建立關聯,嘗試尋找符合下列準則的參數:

-- 此參數必須接受來自管線的輸入 (並非全部皆可)

-- 此參數必須接受傳送物件的類型,或物件可以轉換成的類型。

-- 此參數必須尚未用於該命令中。

例如,Start-Service Cmdlet 有許多參數,但其中只有兩個,也就是 Name 和 InputObject 可接受管線輸入。Name 參數會接受字串,而 InputObject 參數則接受服務物件。因此,您可以輸送字串和服務物件 (以及具有可以轉換成字串和服務物件之屬性的物件) 到 Start-Service。

如果 Windows PowerShell 的參數繫結元件無法將輸送的物件與接收的 Cmdlet 參數建立關聯,則此命令會失敗,且 Windows PowerShell 會提示您輸入遺漏的參數值。

您無法強制讓參數繫結元件使輸送的物件與特定參數建立關聯,您甚至無法建議某個參數。不過,元件的邏輯會盡可能有效率地管理管線。

一次一個的處理

輸送物件給命令更像是使用該命令的參數來提交物件。

例如,將代表電腦上服務的物件輸送到 Format-Table 命令,像是:

  get-service | format-table -property name, dependentservices

更像將服務物件儲存在變數中,和使用 Format-Table 的 InputObject 參數來提交服務物件一樣。

  $services = get-service
                  format-table -inputobject $services -property name, dependentservices

或內嵌命令到參數值

                  format-table -inputobject (get-service wmi) -property name, dependentservices

不過,還有一項重要差異。當您將多個物件輸送給命令時,Windows PowerShell 會以一次一個的方式將物件傳送給命令。當您使用命令參數時,此物件會以單一陣列物件形式傳送。

這項看似技術上的差異,但結果卻相當有趣,且有時會很有用。

例如,如果您將 Get-Process Cmdlet 中多個處理程序物件輸送給 Get-Member Cmdlet,Windows PowerShell 將以一次一個的方式,將每個處理程序物件傳送至 Get-Member。Get-Member 會顯示該處理程序物件的.NET 類別 (類型) 及其屬性和方法。(Get-Member 會排除重複項目,因此如果物件全都具有相同類型,它只會顯示一個物件類型。)

在此情況下,Get-Member 會顯示每個處理程序物件的屬性和方法,也就是 System.Diagnostics.Process 物件。

                 get-process | get-member

                    TypeName: System.Diagnostics.Process

                 Name                           MemberType     Definition
                 ----                           ----------     ----------
                 Handles                        AliasProperty  Handles = Handlecount
                 Name                           AliasProperty  Name = ProcessName
                 NPM                            AliasProperty  NPM = NonpagedSystemMemorySize
                 ...

不過,如果您使用 Get-Member 的 InputObject 參數,則 Get-Member 就會收到 System.Diagnostics.Process 物件的陣列,當做單一單位,而且它會顯示物件陣列的屬性。(請注意陣列符號 ([]) 位在 System.Object 類型名稱後面。)

                get-member -inputobject (get-process)


                TypeName: System.Object[]

                Name               MemberType    Definition
                ----               ----------    ----------
                Count              AliasProperty Count = Length
                Address            Method        System.Object& Address(Int32 )
                Clone              Method        System.Object Clone()

此結果可能不是您所需的,但您了解之後,就可以使用它。例如,處理程序物件的陣列具有 Count 屬性,可讓您在電腦上計算處理程序數目。

(get-process).count

這個差別非常重要,因此請記得當您輸送物件給 Cmdlet 時,一次只能傳遞一個。

接受管線輸入

若要接收管線中的物件,接收端 Cmdlet 必須具有接受管線輸入的參數。您可以藉由 Full 或 Parameter 參數使用 Get-Help 命令,以判斷哪個 Cmdlet 參數可接受管線輸入 (如果有的話)。

在 Get-Help 預設顯示中,[接受管線輸入] 項目會出現在參數屬性的表格。只有當您使用 Get-Help Cmdlet 的 Full 或 Parameter 參數時,才會顯示此資料表。

例如,若要判斷哪一個 Start-Service Cmdlet 的參數會接受管線輸入,請輸入:

        get-help start-service -full

        get-help start-service -parameter *

例如,Start-Service Cmdlet 的說明顯示,Name 和 InputObject 參數接受管線輸入 ("true")。「接受管線輸入?」資料列中所有其他有 "false" 值的參數。

        -name <string[]>
           Specifies the service names for the service to be started.
           The parameter name is optional. You can use "-Name" or its alias, 
           "-ServiceName", or you can omit the parameter name.

           Required?                    true
           Position?                    1
           Default value
      -->  Accept pipeline input?       true (ByValue, ByPropertyName)
           Accept wildcard characters?  true

        -inputObject <ServiceController[]>
           Specifies ServiceController objects representing the services to be started. Enter
           a variable that contains the objects or type a command or expression that gets the
           objects.

           Required?                    false
           Position?                    named
           Default value
      -->  Accept pipeline input?       true (ByValue)
           Accept wildcard characters?  false

這表示您可以透過管線傳送物件 (PsObjects) 到 Where-Object Cmdlet,而且 Windows PowerShell 會將物件與 InputObject 參數建立關聯。

接受管線輸入的方法

Cmdlet 參數可以用兩種不同方式接受管線輸入:

-- ByValue:接受「依值」輸入的參數可以接受輸送的物件具有與其參數值相同的 .NET 類型,或可以轉換為該類型的物件。

例如,Start-Service 的 Name 參數可接受依值的管線輸入。它可接受字串物件或可以轉換為字串的物件。

-- ByPropertyName:只有當物件屬性具有與該參數相同的名稱時,「依屬性名稱」接受輸入的參數才可接受輸送的物件。

例如,Start-Service 的 Name 參數可以接受具有 Name 屬性的物件。

(若要列出物件的屬性,請將其輸送到 Get-Member。)

某些參數可依值或依屬性名稱接受物件。這些參數的設計目的是要用來輕鬆採用來自管線的輸入。

調查管線錯誤

如果因為管線錯誤而讓命令失敗,您可以調查此失敗並且重寫命令。

例如,下列命令會嘗試將登錄項目從某登錄機碼移到另一個,方法是使用 Get-Item Cmdlet 取得目的地路徑,然後將此路徑輸送給 Move-ItemProperty Cmdlet。

具體而言,此命令會使用 Get-Item Cmdlet 來取得目的地路徑。它會使用管線運算子將物件傳送至 Move-ItemProperty Cmdlet。Move-ItemProperty 命令會指定要移動的登錄項目名稱與目前路徑。

          get-item -path hklm:\software\mycompany\sales | 
          move-itemproperty -path hklm:\software\mycompany\design -name product

此命令會失敗,且 Windows PowerShell 會顯示下列錯誤訊息:

         Move-ItemProperty : The input object cannot be bound to any parameters for the
         command either because the command does not take pipeline input or the input
         and its properties do not match any of the parameters that take pipeline input.
         At line:1 char:23
         + $a | move-itemproperty <<<<  -path hklm:\software\mycompany\design -name product

若要調查,請使用 Trace-Command Cmdlet 來追蹤 Windows PowerShell 的參數繫結元件。當該命令正在處理時,下列命令會追蹤參數繫結元件。它會使用 -pshost 參數將結果顯示在主控台,以及 -filepath 命令,將結果傳送至 debug.txt 檔案以供日後參考。

         trace-command -name parameterbinding -expression {get-item -path hklm:\software\mycompany\sales |
             move-itemproperty -path hklm:\software\mycompany\design -name product} -pshost -filepath debug.txt

追蹤的結果很長,但它們會顯示繫結至 Get-Item Cmdlet 的值,然後顯示繫結至 Move-ItemProperty Cmdlet 的具名值。

       ... 
        BIND NAMED cmd line args [Move-ItemProperty]
            BIND arg [hklm:\software\mycompany\design] to parameter [Path]
        ...
            BIND arg [product] to parameter [Name]
        ....
        BIND POSITIONAL cmd line args [Move-ItemProperty]
        ...

最後,它會顯示嘗試將路徑繫結至 Move-ItemProperty 的 Destination 參數失敗。

        ...
        BIND PIPELINE object to parameters: [Move-ItemProperty]
            PIPELINE object TYPE = [Microsoft.Win32.RegistryKey]
            RESTORING pipeline parameter's original values
            Parameter [Destination] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
            Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
        ... 

若要調查失敗,請使用 Get-Help Cmdlet 來檢視 Destination 參數的屬性。下列命令會取得 Destination 參數的詳細資訊。

get-help move-itemproperty -parameter destination

此結果會顯示 Destination 只可接受「依屬性名稱」的管線輸入。也就是說,輸送的物件必須有一個名為 Destination 的屬性。

        -destination <string>
            Specifies the path to the destination location.

            Required?                    true
            Position?                    2
            Default value
            Accept pipeline input?       true (ByPropertyName)
            Accept wildcard characters?  true    

若要查看正在輸送至 Move-ItemProperty Cmdlet 之物件的屬性,請將其輸送到 Get-Member Cmdlet。下列命令會將此命令第一部分的結果輸送至 Get-Member Cmdlet。

          get-item -path hklm:\software\mycompany\sales | get-member

此輸出會顯示此項目是沒有目的地屬性的 Microsoft.Win32.RegistryKey。這也說明了命令失敗的原因。

若要修正此命令,我們必須在 Move-ItemProperty Cmdlet 中指定目的地。我們可以使用 Get-ItemProperty 命令來取得路徑,但是必須在此命令的 Move-ItemProperty 部分中指定名稱和目的地。

         get-item -path hklm:\software\mycompany\design | 
             move-itemproperty -dest hklm:\software\mycompany\design -name product    

若要確定此命令能運作,請使用 Get-ItemProperty 命令:

get-itemproperty hklm:\software\mycompany\sales

該結果會顯示 Product 登錄項目已移至 Sales 索引鍵。

        PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\mycompany\sales
        PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\mycompany
        PSChildName  : sales
        PSDrive      : HKLM
        PSProvider   : Microsoft.PowerShell.Core\Registry
        Product      : 18

另請參閱

about_objects

about_parameters

about_command_syntax

about_foreach