PowerShell管線是PowerShell殼層和腳本語言中最重要(也是最有用)的功能之一。一旦你理解了它的基本工作原理和能力,你就可以利用它的威力來構建自己的函數。
PowerShell管線允許你鏈接命令以構建一個單一的“管線”,簡化代碼,實現並行處理等。如果你準備學習有關管線的知識並構建自己的函數以利用管線的功能,讓我們開始吧!
先決條件
本帖將是一個教程,全部都是實際操作演示。如果你想跟著進行,你需要安裝PowerShell v3+。本教程將使用Windows PowerShell v5.1。
理解PowerShell管線
大多數PowerShell命令通過參數接收一些輸入。該命令接收某個對象作為輸入,並在內部對其執行某些操作。然後,它可以選擇性地通過輸出返回某個對象。
管線中的命令就像接力賽中的人類跑步者一樣。賽中的每個跑步者,除了第一個和最後一個之外,都接收來自前任的接力棒(對象)並將其傳遞給下一個。
例如,Stop-Service
cmdlet 有一個名為InputObject
的參數。此參數允許您傳遞特定類型的物件給Stop-Service
,以表示您想要停止的 Windows 服務。
要使用InputObject
參數,您可以通過Get-Service
檢索服務物件,然後將該物件傳遞給InputObject
參數,如下所示。通過此方法將輸入提供給Stop-Service
cmdlet 的InputObject
參數非常有效且能完成任務。
通過此方法將輸入傳遞給Stop-Service
命令需要兩個不同的步驟。PowerShell 必須先運行Get-Service
,將輸出保存到變量中,然後通過InputObject
參數將該值傳遞給Stop-Service
。
現在,將上面的片段與下面的片段進行對比,它完成相同的工作。這樣做起來簡單得多,因為您不必創建一個$services
變量,也不必使用InputObject
參數。相反,PowerShell “知道”您打算使用InputObject
參數。它通過一個叫做參數繫結的概念實現這一點。
您現在使用了|
運算符將命令連接在一起。您創建了一個pipeline。
但是,您不必僅使用兩個命令來創建 pipeline;您可以鏈接任意多個命令在一起(如果命令參數支持)。例如,以下代碼片段:
- 將
Get-Service
命令返回的所有物件傳遞給Where-Object
命令。 Where-Object
命令然後查看每個物件的Status
屬性,然後僅返回具有值Running
的物件。- 然後,將這些物件中的每個物件發送到
Select-Object
,該命令僅返回物件的Name
和DisplayName
屬性。 - 由於沒有其他命令接受
Select-Object
輸出的物件,因此該命令將物件直接返回到控制台。
Where-Object
和Select-Object
命令了解如何通過稱為參數綁定的概念來處理管道輸入,下一節將進行討論。
有關管道的其他信息,運行命令
Get-Help about_pipelines
。
管道參數綁定
乍一看,管道可能似乎微不足道。畢竟,它只是將物件從一個命令傳遞到另一個命令。但實際上,管道要複雜得多。命令僅通過參數接受輸入。即使您沒有明確定義,管道也必須想辦法確定使用哪個參數。
確定在命令通過管道接收輸入時使用哪個參數的任務稱為參數綁定。要成功地將從管道中進入的對象綁定到參數,傳入命令的參數必須支持它。命令參數通過以下兩種方式之一支持管道參數綁定:ByValue和/或ByPropertyName。
ByValue
命令參數接受整個傳入對象作為參數值。 ByValue參數在傳入對象中尋找特定類型的對象。如果該對象類型匹配,PowerShell會假定該對象意味著要綁定到該參數並接受它。
Get-ChildItem cmdlet有一個名為Path的參數,它接受字符串對象類型並通過ByValue進行管道輸入。因此,運行像’C:\Windows’ | Get-ChildItem這樣的命令將返回C:\Windows目錄中的所有文件,因為C:\Windows是一個字符串。
ByPropertyName
命令參數不接受整個對象,而是該對象的單個屬性。它通過查看屬性名稱而不是對象類型來實現這一點。
Get-Process 命令有一個
Name
參數,設置為接受通道輸入ByPropertyName
。 當你將具有Name
屬性的對象傳遞給Get-Process
命令時,例如[pscustomobject]@{Name='firefox'} | Get-Process
,PowerShell 會將傳入對象上的Name
屬性與Name
參數進行匹配或綁定,並使用該值。
發現支持管道輸入的命令參數
如前所述,並非每個命令都支持管道輸入。 命令作者必須在開發中創建該功能。 該命令必須至少有一個支持管道的參數,使用 ByValue
或 ByPropertyName
。
你怎麼知道哪些命令及其參數支持管道輸入呢? 你可以嘗試通過反覆試驗來嘗試,但使用 PowerShell 幫助系統的 Get-Help
命令有一種更好的方法。
舉例來說,查看 Get-ChildItem
命令的 Path
參數。 你可以看到它支持兩種類型的管道輸入。

一旦您知道哪些命令參數支持管道輸入,您就可以利用該功能,如下所示。
但是,另一方面,Get-Service
上的 DisplayName
參數不支持管道輸入。

構建您自己的管道函數
即使標準的 PowerShell 命令支持管道輸入,也不意味著您不能利用該功能。幸運的是,您也可以構建接受管道輸入的函數。
為了演示,讓我們從一個名為 Get-ConnectionStatus
的現有函數開始。
- 這個函數有一個單一的參數(不接受管道輸入)叫做
ComputerName
,它允許您將一個或多個字符串傳遞給它。您可以看出ComputerName
參數不接受管道輸入,因為它未被定義為參數屬性([Parameter()]
)。 - 然後,該函數會讀取每個字符串並對每個字符串運行
Test-Connection
命令。 - 對於傳遞的每個字串電腦名稱,它將返回一個帶有
ComputerName
和Status
屬性的物件。
然後,通過傳遞ComputerName
參數,呼叫Get-ConnectionStatus
,傳遞一個或多個主機名稱或IP地址,如下所示。
步驟1:允許管道輸入
要使此函數接受管道輸入,您必須首先定義適當的參數屬性。此屬性可以是ValueFromPipeline
以接受通過值的管道輸入,或者ValueFromPipelineByPropertyName
以接受通過屬性名的管道輸入。
在此示例中,在[Parameter()]
定義的括號內添加ValueFromPipeline
參數屬性,如下所示。
在這一點上,那就是技術上您所需要做的。Get-ConnectionStatus
函數現在將任何傳遞給它的字串物件繫結到ComputerName
參數。但是,即使參數繫結正在發生,也不表示函數將對其執行任何有意義的操作。
步驟2:添加處理區塊
當您希望PowerShell處理來自管道的所有物件時,您必須接著添加一個Process
區塊。該區塊告訴PowerShell要處理來自管道的每個物件。
沒有
Process
區塊,PowerShell將僅處理來自管道的第一個物件。Process
區塊告訴PowerShell繼續處理物件。
添加
始終在
Process
區塊內發送輸出。在Process
區塊內發送輸出會將對象流到管線,使其他命令能夠從管線接受這些對象。
將對象傳遞到 PowerShell 管線
一旦在上面的函數中定義了Process
區塊,您現在可以通過管線傳遞值給ComputerName
參數,如下所示。
此時,您可以充分利用管線的真正威力,開始將更多命令納入其中。例如,也許您有一個文本文件C:\Test\computers.txt,其中包含一行IP地址,透過換行分隔,如下所示。
然後,您可以使用Get-Content
命令讀取文本文件中的每個IP地址,並將它們直接傳遞給Get-ConnectionStatus
函數。
將這個設置推進一步,您可以將Get-ConnectionStatus
返回的對象直接傳遞給ForEach-Object
命令。
代碼如下:
- 讀取文本檔中的所有計算機名稱並將它們傳遞給
Get-ConnectionStatus
函數。 Get-ConnectionStatus
處理每個計算機名稱並返回一個具有ComputerName
和Status
屬性的對象。Get-ConnectionStatus
然後將每個對象傳遞給ForEach-Object
cmdlet,該cmdlet然後返回一個以Cyan顏色顯示的人可讀的狀態字符串。
如果在
ComputerName
參數上未啟用管道輸入,或者Get-ConnectionStatus
在Process
塊中沒有返回對象,PowerShell將不會將任何狀態返回到控制台,直到所有對象(IP地址)都被處理。
通過屬性名稱進行管道綁定
到目前為止,Get-ConnectionStatus
cmdlet被設置為接受管道輸入ByValue(ValueFromPipeline
),方法是接受一個字符串數組,如'127.0.0.1', '192.168.1.100'
。如果從CSV文件而不是IP地址的文本文件接收輸入,這個函數是否也會按預期工作?
也許您有一個CSV文件,看起來像下面這樣:C:\Test\pc-list.csv。
請注意,CSV文件中的
ComputerName
字段與Get-ConnnectionStatus
的ComputerName
參數相同。
如果您嘗試導入CSV並將其通過管道傳遞給Get-ConnectionStatus
,該函數將以ComputerName
列返回意外結果。
你能猜到哪裡出了問題嗎?畢竟,參數名稱確實相符,那麼為什麼 PowerShell 管線沒有將 Import-CSV
返回的輸出綁定到 Get-ConnectionStatus
上的 ComputerName
參數呢?因為你需要使用參數屬性 ValueFromPipelineByPropertyName
。
截至目前,函數的 ComputerName
參數具有以下參數定義:[Parameter(ValueFromPipeline)]
。因此,您必須添加 ValueFromPipelineByPropertyName
來將 ComputerName
參數設置為支持按屬性名稱輸入,如下所示。
一旦您啟用了管道支持按屬性名稱,您告訴 PowerShell 開始查看對象屬性名稱和對象類型。一旦您進行了此更改,您應該會看到預期的輸出。
總結
在本教程中,您了解了 PowerShell 管道的工作原理,它如何綁定參數,甚至如何創建支持 PowerShell 管道的自己的函數。
儘管函數可以在沒有管道的情況下工作,但它們不會將對象“流”從一個命令傳送到另一個命令並簡化代碼。
您能想到一個您編寫過的函數,或者即將編寫的函數,可以從使其準備好管道中受益嗎?