主題
    about_pipelines

簡短描述
    將 Windows PowerShell 中的命令組合成管線 

完整描述
    管線是指一系列由管線運算子 (|) (ASCII 124) 連接起來的命令, 每個管線運算子
    會將前一個命令的結果傳送給下一個命令。
     
    您可以使用管線,將某個命令輸出的物件傳送給另一個命令做為輸入,以便進行處
    理。然後,您還可以將該命令的輸出再傳送給其他命令。得到的結果會是由一系列簡
    單命令所組成、非常強大的命令鏈結 (或「管線」)。

    例如,  

	命令 1 | 命令 2 | 命令 3 

    在此例中,命令 1 發出的物件會傳送給命令 2。命令 2 會處理這些物件,接著傳送
    給命令 3。命令 3 接著處理物件,再沿著管線向下傳送。由於管線中沒有其他命令,
    所以會將結果顯示在主控台中。

    在管線中,會從左至右的方向,依照命令出現的順序來處理命令。整個處理程序視同
    單一作業來進行,並在輸出產生時加以顯示。

    這裡提供一個簡單範例。下列命令會取得「記事本」處理程序,接著將它停止。

         get-process notepad | stop-process

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

    以下提供一個實用範例。此命令管線會取得目前目錄中的文字檔,僅選取超過 10,000
    位元組長度的檔案,依據長度排序檔案,然後在表格中顯示每個檔案的名稱和長度。

        Get-ChildItem -path *.txt | Where-Object {$_.length -gt 10000} | 
        Sort-Object -property Length | Format-Table -property name, length 
    此管線是由四個命令依指定的順序所組成, 命令是以水平方式撰寫,但下圖將以垂
    直方式顯示整個程序。

       Get-ChildItem -path *.txt

                  |
                  |  (FileInfo 物件 )
                  |  (    .txt      )
                  |
                  V                   

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

                  |
                  |  (FileInfo 物件 )
                  |  (    .txt      )
                  |  ( 長度 > 10000 )
                  |
                  V

       Sort-Object -property Length

                  |
                  |  (FileInfo 物件 )
                  |  (    .txt      )
                  |  ( 長度 > 10000 )
                  |  (  依長度排序  )
                  |
                  V

       Format-Table -property name, length

                  |  
                  |  (FileInfo 物件   )
                  |  (    .txt        )
                  |  (  長度 > 10000  )
                  |  (   依長度排序   )
                  |  ( 在表格中格式化 )
                  |
                  V
        名稱                       長度
        ----                       ------
        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 的其中一個參數產生關聯。

     為了執行這項操作,負責將輸入物件與 Cmdlet 參數產生關聯的 Windows PowerShell
     「參數繫結」元件會嘗試尋找符合下列條件的參數:
    
     -- 參數必須接受來自管線的輸入 (並非全都是)
     -- 參數必須接受要傳送的物件類型,或該物件可轉換的類型。
     -- 參數不能正由命令使用中。

     例如,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

                 名稱                MemberType    定義
                 ----                ----------     ----------
                 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[]

                名稱               MemberType    定義
                ----               ----------    ----------
                Count              AliasProperty Count = Length
                Address            Method        System.Object& Address(Int32 )
                Clone              Method        System.Object Clone()
                ...

     這個結果可能不是您要的,但當您了解其涵義後,仍然可以使用。例如,處理程序物
     件的陣列具有 Count 屬性,可以用來計數電腦的處理程序數目。

		(get-process).count
                
     這項區別很重要,所以請記住,當您經由管道將物件輸出至 Cmdlet 時,是一次傳遞
     一個物件。


接受管線輸入

    為了接收管線中的物件,接收端 Cmdlet 必須具有接受管線輸入的參數。您可以使用 
    Get-Help 命令搭配 Full 或 Parameter 參數,判斷哪個 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[]>
           指定要啟動之服務的服務名稱。
           參數名稱為選擇性。可使用 "-Name" 或其別名 "-ServiceName",亦可省略
           參數名稱。

           必要?                    true
           位置?                    1
           預設值
      -->  接受管線輸入?            true (ByValue, ByPropertyName)
           接受萬用字元?            true

        -inputObject <ServiceController[]>
           指定表示要啟動之服務的 ServiceController 物件。請輸入包含物件的變
           數,或輸入可取得物件的命令或運算式。

           必要?                    false
           位置?                    named
           預設值
      -->  接受管線輸入?            true (ByValue)
           接受萬用字元?            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 : 輸入物件無法繫結到該命令的任何參數,可能是因為該命
         令不接受管線輸入,或者此輸入及其屬性不符合任何接受管線輸入的參數。
         位於第 1 行,第 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 <字串>
            指定目的地位置的路徑。

            必要?                    true
            位置?                    2
            預設值
            接受管線輸入?            true (ByPropertyName) 
            接受萬用字元?            true  

     若要查看將經由管道輸出至 Move-ItemProperty Cmdlet 之物件的屬性,請經由管道
     將該物件輸出至 Get-Member Cmdlet。下列命令會將命令第一個部分的結果經由管道
     輸出至 Get-Member Cmdlet。

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

     輸出顯示此項目是 Microsoft.Win32.RegistryKey,不具有 Destination 屬性。
     這說明了命令失敗的原因。

     若要修正命令,必須在 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
    help about_foreach




目錄