cmdlets应该为管道中间实现的PowerShellStrongly Encouraged Development Guidelines
,但我怀疑这对于Select-Object
的参数-Last
来说是不可行的。只是因为你无法提前确定最后一个条目。换句话说:您需要等待输入流完成,直到定义最后一个条目
为了证明这一点,我写了一个小脚本:
$Data = 1..5 | ForEach-Object {[pscustomobject]@{Index = "$_"}}
$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object -Last 5 | ForEach-Object { Write-Host 'After' $_.Index }
并将其与Select-Object *
:进行比较
$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object * | ForEach-Object { Write-Host 'After' $_.Index }
带结果(右:Select-Object -Last 5
,左:Select-Object *
(:
-Last 5 *
------- -
Before 1 Before 1
Before 2 After 1
Before 3 Before 2
Before 4 After 2
Before 5 Before 3
After 1 After 3
After 2 Before 4
After 3 After 4
After 4 Before 5
After 5 After 5
尽管这没有记录在案,但我认为我可以从中得出结论,-Last
参数确实会阻塞管道
这没什么大不了的,但我也针对-First
参数进行了测试,得到了一些令人不安的结果。为了更好地显示这一点,我没有选择所有对象,而是只选择**-First 2**
:
$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object -First 2 | ForEach-Object { Write-Host 'After' $_.Index }
Before 1
After 1
Before 2
After 2
请注意,使用-First 2
参数时,以下cmdlet不仅显示两个对象,而且前面的cmdlet(ForEach-Object { Write-Host 'Before' $_.Index; $_ }
(也仅显示2个对象(而不是5个(。
显然,-First
参数直接引用到先前cmdlet的对象中,这与使用-Last 2
参数不同
$Data | ForEach-Object { Write-Host 'Before' $_.Index; $_ } |
Select-Object -Last 2 | ForEach-Object { Write-Host 'After' $_.Index }
Before 1
Before 2
Before 3
Before 4
Before 5
After 4
After 5
当使用Out-Host
而不是Write-Host
cmdlet或将结果发送到变量(如:(时,也会发生这种情况
$Before = ""; $After = ""
$Data | ForEach-Object { $Before += $_.Index; $_ } | Select-Object -First 2 | ForEach-Object { $After += $_.Index }
$Before
$After
这在Windows Powershell(5.1.18362.628
(和Powershell Core(7.0.0
(上都显示
这是个错误吗?
Select-Object
通过欺骗影响上游命令
这听起来可能像个笑话,但事实并非如此。
为了优化流水线流传输性能,Select-Object
使用了一个普通用户无法开发Cmdlet
的技巧——它抛出一个StopUpstreamCommandsException
。
一旦被捕获,运行时(间接(会对前面的所有命令调用StopProcessing()
,但不会将其视为终止错误事件,从而允许下游cmdlet继续执行。
当你在管道的早期有缓慢或计算量大的命令时,这是非常有用的:
# this will only take ~3 seconds to return with the StopUpstreamCommand behavior
# but would have incurred 8 extra seconds of "waiting to discard" otherwise
Measure-Command {
1..5 |ForEach-Object { Start-Sleep -Seconds 1; $_ } |Select-Object -First 3
}