about_Pipelines

简短说明

将命令合并到 PowerShell 中的管道中

长说明

管道由管道操作员 () (| ASCII 124) 连接的一系列命令。 每个管道运算符将上述命令的结果发送到下一个命令。

可以将第一个命令的输出作为输入发送到第二个命令。 该输出可以发送到另一个命令。 结果是由一系列简单命令组成的复杂命令链或 管道

例如,

Command-1 | Command-2 | Command-3

在此示例中,将发送到Command-2发出的对象Command-1Command-2 处理对象并将其发送到 Command-3Command-3 处理对象并将其发送到管道。 由于管道中没有更多命令,因此结果将显示在控制台中。

在管道中,命令按从左到右的顺序进行处理。 处理作为单个操作进行处理,输出在生成时显示。

下面是一个简单的示例。 以下命令获取记事本进程,然后停止它。

例如,

Get-Process notepad | Stop-Process

第一个命令使用 Get-Process cmdlet 获取表示记事本进程的对象。 它使用管道运算符 (|) 将进程对象 Stop-Process 发送到 cmdlet,该 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

例如,可以通过管道将 PowerShell 注册表提供程序的Get-ItemGet-ChildItem输出传递给 New-ItemProperty cmdlet。 此示例将值为 8124 的新注册表项 NoOfEmployees 添加到 MyCompany 注册表项。

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

许多实用工具 cmdlet(例如Get-MemberWhere-ObjectSort-Object、和)Measure-ObjectGroup-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

重要

成功错误流类似于其他 shell 的 stdin 和 stderr 流。 但是,stdin 未连接到用于输入的 PowerShell 管道。 有关详细信息,请参阅 about_Redirection

通过一些实践,你会发现将简单的命令合并到管道中可以节省时间和键入,并使脚本更高效。

管道的工作原理

本部分介绍如何在管道执行期间将输入对象绑定到 cmdlet 参数并进行处理。

接受管道输入

若要支持管道传送,接收 cmdlet 必须具有接受管道输入的参数。 使用 Get-Help 包含 “完整 ”或“ 参数 ”选项的命令来确定 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参数。 它可以接受可转换为字符串的字符串对象或对象。

  • ByPropertyName:仅当输入对象具有与参数同名的属性时,参数才接受输入。

    例如,Name 参数 Start-Service 可以接受具有 Name 属性的对象。 若要列出对象的属性,请通过管道将其传递给 Get-Member

某些参数可以通过值或属性名称接受对象,以便更轻松地从管道获取输入。

参数绑定

将对象从一个命令传递给另一个命令时,PowerShell 会尝试将管道对象与接收 cmdlet 的参数相关联。

PowerShell 的参数绑定组件根据以下条件将输入对象与 cmdlet 参数相关联:

  • 该参数必须接受来自管道的输入。
  • 该参数必须接受要发送的对象类型或可转换为预期类型的类型。
  • 该参数未在命令中使用。

例如, Start-Service cmdlet 具有许多参数,但其中只有两个参数, NameInputObject 接受管道输入。 Name 参数采用字符串,InputObject 参数采用服务对象。 因此,可以使用可转换为字符串或服务对象的属性通过管道传递字符串、服务对象和对象。

PowerShell 尽可能高效地管理参数绑定。 无法建议或强制 PowerShell 绑定到特定参数。 如果 PowerShell 无法绑定管道对象,该命令将失败。

有关排查绑定错误的详细信息,请参阅本文后面的 “调查管道错误 ”。

一次性处理

将对象管道到命令就像使用命令的参数提交对象一样。 让我们看看一个管道示例。 在此示例中,我们使用管道来显示服务对象的表。

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

在功能上,这类似于使用 InputObject 参数 Format-Table 提交对象集合。

例如,我们可以将服务集合保存到使用 InputObject 参数传递的变量。

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

或者,我们可以在 InputObject 参数中嵌入命令。

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

但是,有一个重要区别。 通过管道将多个对象传递给命令时,PowerShell 会一次将对象发送到命令。 使用命令参数时,对象将作为单个数组对象发送。 这种细微差异具有重大后果。

执行管道时,PowerShell 会自动枚举实现 IEnumerable 接口的任何类型,并逐个通过管道发送成员。 异常是 [hashtable],需要调用 GetEnumerator() 该方法。

在以下示例中,数组和哈希表通过管道传递给 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-Member该对象。 Get-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 消除重复项,因此,如果对象都是同一类型,则仅显示一个对象类型。

但是,如果使用 InputObject 参数 Get-Member,则将 Get-MemberSystem.Diagnostics.Process 对象的数组作为单个单元接收。 它显示对象的数组的属性。 (记下 System.Object 类型 name.) 后面的数组符号 ([])

例如,

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 字符串。 此转换可能会导致原始数据输出损坏。

解决方法是使用cmd.exe /c本机 shell 提供的本机命令或使用sh -c|本机 shell 提供的和>运算符。

调查管道错误

当 PowerShell 无法将管道对象与接收 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

该命令失败,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
}

跟踪的结果很长,但它们显示 Get-Item 绑定到 cmdlet 的值,然后显示绑定到 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`]
...

最后,它显示尝试将路径绑定到失败的目标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 该路径获取要移动的项 的路径

例如,

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

重要

在 shell 中以交互方式工作时,仅在使用 Ctrl+V 粘贴时,才在行的开头粘贴管道代码。 右键单击粘贴操作一次插入一行。 由于该行不以管道字符结尾,因此 PowerShell 会将输入视为已完成,并按输入执行该行。

另请参阅