主题
    about_pipelines

简短说明
    在 Windows PowerShell 中将命令合并到管道中

详细说明
    管道是由管道运算符 (|)(ASCII 124) 连接的一连串命令。每个管道运算符将前一条命令的
    结果发给下一条命令。
     
    可以使用管道将一条命令输出的对象作为另一条命令的输入发给该命令,以供其处理。
    然后可以将该命令的输出再发送给另一条命令。其结果是得到一个由一系列简单命令组成的
    非常强大的命令链,或称"管道"。

    例如,

	Command-1 | Command-2 | Command-3

    此示例中,Command-1 发出的对象发送给 Command-2。Command-2 处理这些对象,然后将其发送给 
    Command-3。Command-3 接着处理这些对象,再将它们沿管道向下发送。由于管道中已没有其他
    命令,因此控制台上将显示结果。

    在管道中,按命令出现顺序从左到右处理这些命令。处理过程是作为单个操作进行的,
    并且一生成输出就会显示该输出。

    下面是一个简单的示例。下面的命令获取 Notepad 进程,然后将其停止。

         get-process notepad |stop-process

    第一条命令使用 Get-Process cmdlet 获取表示 Notepad 进程的对象。它使用管道运算符 (|) 将该进程对
    象发送给 Stop-Process cmdlet,后者将停止 Notepad 进程。请注意 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
        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。下面的命令将值为 8124 的新注册表条目 NoOfEmployees 添加到 
    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)、导出 cmdlet
    (如 Export-Clixml 和 Export-CSV)和输出 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 默认显示中,“Accepts pipeline input”项出现在参数属性表中。只有在
    使用了 Get-Help cmdlet 的 Full 或 Parameter 参数的情况下,此表才会显示。

    例如,为了确定 Start-Service cmdlet 的哪个参数接受管道输入,请键入:
       
        get-help start-service -full

        get-help start-service -parameter *

    例如,Start-Service cmdlet 的帮助显示 Name 和 InputObject 参数接受管道输入 ("true")。所有其
    他参数在"Accept pipeline input?"行中的值都为"false"。

        -name <string[]>
           为要启动的服务指定服务名称。
           参数名称是可选的。可使用“-Name”或其别名 
           “-ServiceName”,或者,可以省略该参数名称。

           必选?                    true
           位置?                    1
           默认值
      -->  是否接受管道输入?        true (ByValue, ByPropertyName)
           是否接受通配符?          true

        -inputObject <ServiceController[]>
           指定代表要启动的服务的 ServiceController 对象。
           请输入包含对象的变量或键入获取对象的命令或表达式。

           必选?                    false
           位置?                    named
           默认值
      -->  是否接受管道输入?        true (ByValue)
           是否接受通配符?          false

     这意味着您可以通过管道向 Where-Object cmdlet 发送对象 (PsObjects) 
     而 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 跟踪 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 

     输出显示该项是一个没有 Destination 属性的 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





目录