about_Pipelines

簡短描述

將命令結合至 PowerShell 中的管線

詳細描述

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

第一個命令的輸出可以傳送給第二個命令做為輸入進行處理。 而且該輸出可以傳送至另一個命令。 結果是由一系列簡單命令組成的複雜命令鏈結或 管線

例如,

Command-1 | Command-2 | Command-3

在這裡範例中,發出的物件 Command-1 會傳送至 Command-2Command-2 處理物件,並將其傳送至 Command-3Command-3 處理物件,並將其傳送至管線。 因為管線中沒有其他命令,因此結果會顯示在控制台中。

在管線中,命令會依左至右的順序進行處理。 處理會以單一作業的形式處理,而輸出會顯示為產生的輸出。

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

例如,

Get-Process notepad | Stop-Process

第一個命令會Get-Process使用 Cmdlet 來取得代表 記事本 進程的物件。 它會使用管線運算符 (|) 將進程對象傳送至 Stop-Process Cmdlet,這會停止 記事本 進程。 請注意, Stop-Process 命令沒有 NameID 參數可指定進程,因為指定的進程是透過管線送出。

此管線範例會取得目前目錄中的文本檔、只選取長度超過 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 objects for *.txt)
V
Where-Object {$_.length -gt 10000}
| (FileInfo objects for *.txt)
| (      Length > 10000      )
V
Sort-Object -Property Length
| (FileInfo objects for *.txt)
| (      Length > 10000      )
| (     Sorted by length     )
V
Format-Table -Property name, length
| (FileInfo objects for *.txt)
| (      Length > 10000      )
| (     Sorted by length     )
| (   Formatted in a table   )
V

Name                       Length
----                       ------
tmp1.txt                    82920
tmp2.txt                   114000
tmp3.txt                   114000

使用管線

大部分的PowerShell Cmdlet都是設計來支持管線。 在大部分情況下,您可以使用管線將 Get Cmdlet 的結果傳送至相同名詞的另一個 Cmdlet。 例如,您可以使用管線將 Cmdlet 的 Get-Service 輸出傳送至 Start-ServiceStop-Service Cmdlet。

這個範例管線會在電腦上啟動 WMI 服務:

Get-Service wmi | Start-Service

如需另一個範例,您可以將 的輸出Get-Item管線傳送至 New-ItemProperty Cmdlet,或在 Get-ChildItem PowerShell 登錄提供者內。 本範例會將值為8124的新登錄專案 NoOfEmployees 新增至 MyCompany 登錄機碼。

Get-Item -Path HKLM:\Software\MyCompany |
  New-ItemProperty -Name NoOfEmployees -Value 8124

許多公用程式 Cmdlet,例如 Get-MemberWhere-ObjectSort-ObjectGroup-ObjectMeasure-Object 幾乎完全用於管線中。 您可以使用管線將任何物件類型傳送至這些 Cmdlet。 這個範例示範如何依每個進程中開啟的句柄數目排序計算機上的所有進程。

Get-Process | Sort-Object -Property handles

您可以使用管線將物件傳送至格式化、匯出和輸出 Cmdlet,例如 Format-ListFormat-TableExport-ClixmlExport-CSVOut-File

這個範例示範如何使用 Format-List Cmdlet 來顯示進程對象的屬性清單。

Get-Process winlogon | Format-List -Property *

您也可以使用管線將原生命令的輸出傳送至 PowerShell Cmdlet。 例如:

PS> ipconfig.exe | Select-String -Pattern 'IPv4'

   IPv4 Address. . . . . . . . . . . : 172.24.80.1
   IPv4 Address. . . . . . . . . . . : 192.168.1.45
   IPv4 Address. . . . . . . . . . . : 100.64.108.37

重要

成功錯誤數據流類似於其他殼層的 stdin 和 stderr 數據流。 不過,stdin 未連線到 PowerShell 管線以進行輸入。 如需詳細資訊,請參閱 about_Redirection

透過一些練習,您會發現將簡單的命令結合到管線可節省時間和輸入,並讓您的腳本更有效率。

管線的運作方式

本節說明輸入物件如何系結至 Cmdlet 參數,並在管線執行期間進行處理。

接受管線輸入

若要支援管道傳送,接收 Cmdlet 必須有接受管線輸入的參數。 使用 Get-Help 命令搭配 FullParameter 選項來判斷 Cmdlet 接受管線輸入的參數。

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

Get-Help Start-Service -Full

Get-Help Start-Service -Parameter *

Cmdlet 的說明 Start-Service 會顯示只有 InputObjectName 參數接受管線輸入。

-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?                    true
Position?                    0
Default value                None
Accept pipeline input?       True (ByValue)
Accept wildcard characters?  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?                    0
Default value                None
Accept pipeline input?       True (ByPropertyName, ByValue)
Accept wildcard characters?  false

當您透過管線將對象傳送至 Start-Service時,PowerShell 會嘗試將物件與 InputObjectName 參數產生關聯。

接受管線輸入的方法

Cmdlet 參數可以使用兩種不同的方式之一來接受管線輸入:

  • ByValue:參數接受符合預期的 .NET 類型或可轉換成該類型的值。

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

  • ByPropertyName:只有當輸入物件具有與參數相同名稱的屬性時,參數才會接受輸入。

    例如,的 Start-Service Name 參數可以接受具有 Name 屬性的物件。 若要列出物件的屬性,請將它傳送至 Get-Member

某些參數可以接受值或屬性名稱的物件,讓您更輕鬆地從管線取得輸入。

參數繫結

當您使用管線將對象從某個命令傳送至另一個命令時,PowerShell 會嘗試將管道物件與接收 Cmdlet 的參數產生關聯。

PowerShell 的參數係結元件會根據下列準則,將輸入物件與 Cmdlet 參數產生關聯:

  • 參數必須接受來自管線的輸入。
  • 參數必須接受所傳送的物件類型,或可以轉換成預期型別的類型。
  • 命令中未使用 參數。

例如, Start-Service Cmdlet 有許多參數,但其中只有兩個參數: NameInputObject 接受管線輸入。 Name 參數會採用字串,而 InputObject 參數會採用服務物件。 因此,您可以使用可轉換成字串或服務對象的屬性,以管線傳送字串、服務對象和物件。

PowerShell 會盡可能有效率地管理參數係結。 您無法建議或強制 PowerShell 系結至特定參數。 如果 PowerShell 無法繫結管道物件,命令就會失敗。

如需疑難解答系結錯誤的詳細資訊,請參閱 本文稍後的調查管線錯誤

一次一次處理

將物件管線傳送至命令,就像使用 命令的參數來提交對象一樣。 讓我們看看管線範例。 在此範例中,我們使用管線來顯示服務對象的數據表。

Get-Service | Format-Table -Property Name, DependentServices

在功能上,這就像使用Format-Table InputObject 參數來提交物件集合一樣。

例如,我們可以將服務的集合儲存至使用 InputObject 參數傳遞的變數。

$services = Get-Service
Format-Table -InputObject $services -Property Name, DependentServices

或者,我們可以在 InputObject 參數中內嵌命令。

Format-Table -InputObject (Get-Service) -Property Name, DependentServices

不過,有一個重要的差異。 當您使用管線將多個物件傳送至命令時,PowerShell 會一次將對象傳送至一個命令。 當您使用命令參數時,物件會以單一陣物件的形式傳送。 這種輕微的差異會產生重大後果。

執行管線時,PowerShell 會自動列舉任何實作介面或其泛型對應項目的類型 IEnumerable 。 列舉專案會一次透過管線傳送一個。 PowerShell 也會透過 Rows 屬性列舉 System.Data.DataTable 類型。

自動列舉有幾個例外狀況。

  • 您必須針對哈希表、實IDictionary作介面或其泛型對應項目的類型,以及 System.Xml.XmlNode 類型呼叫 GetEnumerator() 方法。
  • System.String 類別會實作 IEnumerable,不過 PowerShell 不會列舉字串物件。

在下列範例中,陣列和哈希表會透過管線傳送至 Measure-Object Cmdlet,以計算從管線接收的物件數目。 數位具有多個成員,而哈希錶具有多個索引鍵/值組。 一次只會列舉一個陣列。

@(1,2,3) | Measure-Object
Count    : 3
Average  :
Sum      :
Maximum  :
Minimum  :
Property :
@{"One"=1;"Two"=2} | Measure-Object
Count    : 1
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

同樣地,如果您透過管線將多個進程物件從 Get-Process Cmdlet 傳送至 Get-Member Cmdlet,PowerShell 就會將每個進程物件一次一個傳送至 Get-MemberGet-Member 會顯示進程物件的 .NET 類別(類型),以及其屬性和方法。

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 會排除重複專案,因此如果物件全都是相同的類型,它只會顯示一個物件類型。

不過,如果您使用Get-MemberInputObject 參數,則會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

請務必記住,傳送至管線的物件會一次傳遞一個。

在管線中使用原生命令

PowerShell 可讓您在管線中包含原生外部命令。 不過,請務必注意 PowerShell 的管線是面向物件,且不支援原始位元組數據。

從原始位元組資料輸出的原生程式進行管線或重新導向輸出,會將輸出轉換成 .NET 字串。 此轉換可能會導致原始數據輸出損毀。

不過,PowerShell 7.4 新增PSNativeCommandPreserveBytePipe了實驗功能,在將原生命令的 stdout 數據流重新導向至檔案時,或在將位元組數據流數據傳送至原生命令的 stdin 數據流時,保留位元組數據流數據的實驗性功能。

例如,使用原生命令 curl ,您可以下載二進位檔,並使用重新導向將它儲存到磁碟。

$uri = 'https://github.com/PowerShell/PowerShell/releases/download/v7.3.4/powershell-7.3.4-linux-arm64.tar.gz'

# native command redirected to a file
curl -s -L $uri > powershell.tar.gz

您也可以使用管線將位元組數據流數據傳送至 另一個原生命令的 stdin 數據流。 下列範例會使用 curl下載壓縮的 TAR 檔案。 下載的檔案數據會串流至 命令, tar 以擷取封存的內容。

# native command output piped to a native command
curl -s -L $uri | tar -xzvf - -C .

您也可以使用管線將PowerShell命令的位元組資料流輸出傳送至原生命令的輸入。 下列範例會使用 Invoke-WebRequest 來下載與上一個範例相同的 TAR 檔案。

# byte stream piped to a native command
(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

# bytes piped to a native command (all at once as byte[])
,(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

將 stderr 輸出重新導向stdout 時,此功能不支援位元組數據流數據。 當您合併 stderrstdout 數據流時,合併的數據流會被視為字串數據。

調查管線錯誤

當 PowerShell 無法將管道物件與接收 Cmdlet 的參數產生關聯時,命令會失敗。

在下列範例中,我們會嘗試將登錄專案從一個登錄機碼移至另一個登錄機碼。 Cmdlet 會 Get-Item 取得目的地路徑,然後透過管線傳送至 Move-ItemProperty Cmdlet。 Move-ItemProperty命令會指定要移動之登錄專案的目前路徑和名稱。

Get-Item -Path HKLM:\Software\MyCompany\sales |
Move-ItemProperty -Path HKLM:\Software\MyCompany\design -Name product

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

Move-ItemProperty : The input object can't be bound to any parameters for
the command either because the command doesn't 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 p

若要調查,請使用 Trace-Command Cmdlet 來追蹤 PowerShell 的參數系結元件。 下列範例會在管線執行時追蹤參數係結。 PSHost 參數會在控制台中顯示追蹤結果,而 FilePath 參數會將追蹤結果傳送至檔案以供debug.txt稍後參考。

Trace-Command -Name ParameterBinding -PSHost -FilePath debug.txt -Expression {
  Get-Item -Path HKLM:\Software\MyCompany\sales |
    Move-ItemProperty -Path HKLM:\Software\MyCompany\design -Name product
}

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

...
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`]
...

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

...
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 參數的屬性

Get-Help Move-ItemProperty -Parameter Destination

-Destination <String>
    Specifies the path to the destination location.

    Required?                    true
    Position?                    1
    Default value                None
    Accept pipeline input?       True (ByPropertyName)
    Accept wildcard characters?  false

結果顯示 目的地 只接受管線輸入「依屬性名稱」。 因此,管道對象必須具有名為 Destination 的屬性。

用來 Get-Member 查看來自 Get-Item的物件屬性。

Get-Item -Path HKLM:\Software\MyCompany\sales | Get-Member

輸出顯示項目是沒有 Destination 屬性的 Microsoft.Win32.RegistryKey 物件。 這說明命令失敗的原因。

Path 參數會依名稱或值接受管線輸入。

Get-Help Move-ItemProperty -Parameter Path

-Path <String[]>
    Specifies the path to the current location of the property. Wildcard
    characters are permitted.

    Required?                    true
    Position?                    0
    Default value                None
    Accept pipeline input?       True (ByPropertyName, ByValue)
    Accept wildcard characters?  true

若要修正命令,我們必須在 Cmdlet 中 Move-ItemProperty 指定目的地,並使用 Get-Item 來取得 我們想要移動之專案的 Path

例如,

Get-Item -Path HKLM:\Software\MyCompany\design |
Move-ItemProperty -Destination HKLM:\Software\MyCompany\sales -Name product

內建線條接續

如先前所述,管線是由管線運算子所連接的一系列命令,|通常是在單一行上撰寫。 不過,為了可讀性,PowerShell 可讓您將管線分割成多行。 當管道運算符是該行的最後一個令牌時,PowerShell 剖析器會將下一行聯結至目前的命令,以繼續建構管線。

例如,下列單行管線:

Command-1 | Command-2 | Command-3

可以撰寫為:

Command-1 |
    Command-2 |
    Command-3

後續行的前置空格並不重要。 縮排可增強可讀性。

PowerShell 7 新增了在行開頭具有管線字元之管線接續的支援。 下列範例示範如何使用這項新功能。

# Wrapping with a pipe at the beginning of a line (no backtick required)
Get-Process | Where-Object CPU | Where-Object Path
    | Get-Item | Where-Object FullName -match "AppData"
    | Sort-Object FullName -Unique

# Wrapping with a pipe on a line by itself
Get-Process | Where-Object CPU | Where-Object Path
    |
    Get-Item | Where-Object FullName -match "AppData"
    |
    Sort-Object FullName -Unique

重要

在殼層中以互動方式運作時,只有在使用 Ctrl+V 貼上時,才會在行開頭貼上管線的程式代碼。 以滑鼠右鍵按下貼上作業,一次插入一行。 由於該行不是以管線字元結尾,因此 PowerShell 會將輸入視為完成,並依輸入執行該行。

另請參閱