使用以下代码,$t
等于@(1,2)
。
$t = "before"
1..2 | Tee-Object -Variable t
那么为什么下一个代码片段$t
等于"before"
而不是@(1)
*?
$t = "before"
1..2 | Tee-Object -Variable t | Select-Object -First 1
我在 Powershell 版本 5 和版本 3 中看到了相同的结果。
使用以下代码,$t
等于@(1,2)
。
$t = "before"
1..2 | Tee-Object -Variable t
那么为什么下一个代码片段$t
等于"before"
而不是@(1)
*?
$t = "before"
1..2 | Tee-Object -Variable t | Select-Object -First 1
我在 Powershell 版本 5 和版本 3 中看到了相同的结果。
这与管道的工作方式有关。如果您使用不同的 cmdlet,Write-Verbose
它将按预期工作:
$t = "before"
1..2 | Tee-Object -Variable t | Write-Verbose -Verbose
考虑在管道期间,每个函数或 cmdlet 都可以使用Begin
、Process
和End
块。
所有Begin
块首先运行,然后Process
为每个管道对象调用一次,然后是所有End
s.
变量赋值必须发生在End
块中。
从 PowerShell v3 开始,cmdlet 可以中断管道。
这对于类似的事情非常有用,Select-Object -First 1
因为这意味着可以在第一个对象之后停止整个管道,而不是为每个项目执行一次,即使其余的将被丢弃。
但这也意味着该End
块永远不会运行。
如果您在 v2 中启动 powershell:powershell.exe -Version 2.0
然后运行您的第二个示例,它将按预期工作,因为管道无法在该版本中过早停止。
这是一个演示:
function F1 {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
$o
)
Begin {
Write-Verbose "Begin" -Verbose
}
Process {
Write-Verbose $o -Verbose
$o
}
End {
Write-Verbose "End" -Verbose
}
}
然后调用它:
1..2 | F1
对比
1..2 | F1 | Select-Object -First 1
您还可以通过以下方式证明这一点ForEach-Object
:
1..2 | ForEach-Object -Begin {
Write-Verbose "Begin" -Verbose
} -Process {
Write-Verbose $_ -Verbose
$_
} -End {
Write-Verbose "End" -Verbose
}
对比
1..2 | ForEach-Object -Begin {
Write-Verbose "Begin" -Verbose
} -Process {
Write-Verbose $_ -Verbose
$_
} -End {
Write-Verbose "End" -Verbose
} | Select-Object -First 1
根据您链接的文档,您可以使用该-Wait
参数来关闭此优化:
$t = "before"
1..2 | Tee-Object -Variable t | Select-Object -First 1 -Wait
这将填充$t
但可能不是您想要的值。它将包含@(1,2)
,大概是因为-OutVariable
被放置在Tee-Object
而不是Select-Object
。
请记住,管道的“结果”是从执行中返回的(左侧=
),并且在所有情况下都是正确的。
-OutVariable
是一些 cmdlet 实现的东西,并且很可能必须在End
该特定 cmdlet 的块中实现,因此预测它将提供什么高度依赖于理解管道的执行流程。
因此,要回答您评论中的问题,在我看来,它的实施是正确的。我误解了你的说法吗?