我了解 PowerShell 管道通过获取一个 cmdlet 的输出并将其作为输入传递给另一个 cmdlet 来工作。但它是如何做到这一点的呢?
第一个 cmdlet 是否完成,然后一次传递所有输出变量,然后由下一个 cmdlet 处理?
还是第一个 cmdlet 的每个输出一次获取一个,然后通过所有剩余的管道 cmdlet 运行它?
我了解 PowerShell 管道通过获取一个 cmdlet 的输出并将其作为输入传递给另一个 cmdlet 来工作。但它是如何做到这一点的呢?
第一个 cmdlet 是否完成,然后一次传递所有输出变量,然后由下一个 cmdlet 处理?
还是第一个 cmdlet 的每个输出一次获取一个,然后通过所有剩余的管道 cmdlet 运行它?
您可以通过一个简单的脚本了解管道顺序是如何工作的:
function a {begin {Write-Host 'begin a'} process {Write-Host "process a: $_"; $_} end {Write-Host 'end a'}}
function b {begin {Write-Host 'begin b'} process {Write-Host "process b: $_"; $_} end {Write-Host 'end b'}}
function c { Write-Host 'c' }
1..3 | a | b | c
输出:
begin a
begin b
process a: 1
process b: 1
process a: 2
process b: 2
process a: 3
process b: 3
end a
end b
c
Powershell 管道以异步方式工作。这意味着第一个 cmdlet 的输出可立即用于第二个 cmdlet 的一个对象(即使第一个 cmdlet 尚未完成执行)。
例如,如果您运行以下行:
dir -recurse| out-file C:\a.txt
然后按 Control+C 停止执行,您将看到部分目录已写入文本文件。
一个更好的例子是下面的代码:(这对于删除驱动器 c 上的所有 .tmp 文件确实很有用:)
get-childitem c:\ -include *.tmp -recurse | foreach ($_) {remove-item $_.fullname}
每次 $_ 在第二个 cmdlet 中获取一个(单个文件)的值
到目前为止,这两个答案都为您提供了一些关于流水线的好信息。但是,还有更多要说的。
首先,为了直接解决您的问题,您提出了管道可能工作的两种可能方式。他们都是对的……取决于管道两侧的 cmdlet!但是,管道应该工作的方式更接近您的第二个概念:一次处理一个对象。(虽然不能保证一个对象会在下一个对象启动之前一直通过,因为管道中的每个组件都是异步的,正如 S Nash 提到的那样。)
那么“这取决于您的 cmdlet”是什么意思?如果您谈论的是 Microsoft 提供的 cmdlet,它们可能都按您的预期工作,尽可能高效地通过管道传递每个对象。但是,如果您谈论的是您编写的 cmdlet,则取决于您如何编写它们:编写无法进行正确流水线操作的 cmdlet 与编写成功的 cmdlet 一样容易!
有两种主要的失效模式:
当然,您要争取的是在收到每个输入后立即处理它,并在确定后立即发出其输出。有关所有这些的详细示例,请参阅我刚刚在 Simple-Talk.com 上发表的文章《PowerShell 管道的来龙去脉》。