【问题标题】:Select-Object -First affects prior cmdlet in the pipelineSelect-Object -First 影响管道中的先前 cmdlet
【发布时间】:2020-07-02 13:00:00
【问题描述】:

PowerShell Strongly Encouraged Development Guidelines cmdlet 应该是 Implement for the Middle of a Pipeline 但我怀疑这对于 -Last 的参数是不可行的 Select-Object。仅仅是因为您无法预先确定最后一个条目。换句话说:您需要等待输入流完成,直到您定义最后一个条目。
为了证明这一点,我写了一个小脚本:

$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) 上。
这是一个错误吗?

【问题讨论】:

  • Sort-ObjectGroup-ObjectSelect-Object [带有“全部需要”参数之一] _要求管道阶段发送“不再有项目”之前的前一个做需要的事情。这是合乎逻辑的......我认为它已记录在案,但我目前找不到。
  • "这是一个错误吗?" - what 是一个错误吗?您描述了一些观察到的行为,并且您似乎完全有能力推理为什么,但我没有看到任何东西......坏了?
  • @Mathias,抱歉,不是很清楚,但“-First 2”的问题是前面的 cmdlet 还显示 2 个对象,而不是最初的 5 (我已将此添加到问题中)。
  • @iRon 啊,明白了!有一个很好的解释!
  • Select-object 不会阻塞,但背景中的 format-table 会阻塞。我喜欢 select-object -first 杀死管道的功能。 select-object -last 会阻塞是有道理的。

标签: powershell pipeline select-object


【解决方案1】:

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
}

【讨论】:

  • 如果由于某种原因您希望它仍然处理所有管道对象,即使它不会输出其中一些对象,您可以将-Wait 参数添加到Select-Object。这也显然可以防止StopUpstreamCommandsException 被抛出。
猜你喜欢
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
  • 2017-07-11
  • 2012-02-03
  • 1970-01-01
  • 2019-12-31
  • 2014-07-04
  • 1970-01-01
相关资源
最近更新 更多