使用以下代码,$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中看到相同的结果。
答案 0 :(得分:9)
这与管道的工作方式有关。如果您使用其他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
VS
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
}
VS
1..2 | ForEach-Object -Begin {
Write-Verbose "Begin" -Verbose
} -Process {
Write-Verbose $_ -Verbose
$_
} -End {
Write-Verbose "End" -Verbose
} | Select-Object -First 1
根据the documentation you linked,您可以使用-Wait
参数关闭此优化:
$t = "before"
1..2 | Tee-Object -Variable t | Select-Object -First 1 -Wait
这将填充$t
,但可能不会填充您想要的值。它将包含@(1,2)
,大概是因为-OutVariable
放在Tee-Object
而不是Select-Object
。
请记住"结果"管道是从执行中返回的(=
的左侧),这在所有情况下都是正确的。
-OutVariable
是由某些cmdlet实现的,并且很可能必须在该特定cmdlet的End
块中实现,因此预测它将给出的内容高度依赖于理解执行管道的流动。
因此,为了回答您的评论中的问题,我觉得可以正确实施。我误解了你的断言吗?