Select-Object 如何在 PowerShell v3 中停止管道? [英] How does Select-Object stop the pipeline in PowerShell v3?

查看:77
本文介绍了Select-Object 如何在 PowerShell v3 中停止管道?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 PowerShell v2 中,以下行:

In PowerShell v2, the following line:

1..3| foreach { Write-Host "Value : $_"; $_ }| select -First 1

会显示:

Value : 1
1
Value : 2
Value : 3

因为所有元素都被推到了管道中.但是,在 v3 中仅显示上述行:

Since all elements were pushed down the pipeline. However, in v3 the above line displays only:

Value : 1
1

管道在 2 和 3 发送到 Foreach-Object 之前停止(注意:Select-Object-Wait 开关允许到达 foreach 块的所有元素).

The pipeline is stopped before 2 and 3 are sent to Foreach-Object (Note: the -Wait switch for Select-Object allows all elements to reach the foreach block).

Select-Object 如何停止管道,我现在可以从 foreach 或我自己的函数停止管道吗?

How does Select-Object stop the pipeline, and can I now stop the pipeline from a foreach or from my own function?

我知道我可以将管道包装在 do...while 循环中并继续离开管道.我还发现在 v3 中我可以做这样的事情(它在 v2 中不起作用):

I know I can wrap a pipeline in a do...while loop and continue out of the pipeline. I have also found that in v3 I can do something like this (it doesn't work in v2):

function Start-Enumerate ($array) {
    do{ $array } while($false)  
}

Start-Enumerate (1..3)| foreach {if($_ -ge 2){break};$_}; 'V2 Will Not Get Here'

但是 Select-Object 不需要这些技术中的任何一种,所以我希望有一种方法可以从管道中的单个点停止管道.

But Select-Object doesn't require either of these techniques so I was hoping that there was a way to stop the pipeline from a single point in the pipeline.

推荐答案

在尝试了几种方法后,包括抛出 StopUpstreamCommandsException、ActionPreferenceStopException 和 PipelineClosedException,调用 $PSCmdlet.ThrowTerminatingError 和 $ExecutionContext.Host.Runspace.GetCurrentlyRunningPipeline().stopper.set_IsStopping($true) 我终于发现仅使用 select-object 是唯一不会中止整个脚本(而不是管道)的东西.[请注意,上面提到的一些项目需要访问私有成员,我通过反射访问.]

After trying several methods, including throwing StopUpstreamCommandsException, ActionPreferenceStopException, and PipelineClosedException, calling $PSCmdlet.ThrowTerminatingError and $ExecutionContext.Host.Runspace.GetCurrentlyRunningPipeline().stopper.set_IsStopping($true) I finally found that just utilizing select-object was the only thing that didn't abort the whole script (versus just the pipeline). [Note that some of the items mentioned above require access to private members, which I accessed via reflection.]

# This looks like it should put a zero in the pipeline but on PS 3.0 it doesn't
function stop-pipeline {
  $sp = {select-object -f 1}.GetSteppablePipeline($MyInvocation.CommandOrigin)
  $sp.Begin($true)
  $x = $sp.Process(0) # this call doesn't return
  $sp.End()
}

<小时>

根据 OP 的评论遵循新方法.不幸的是,这种方法要复杂得多并且使用私有成员.此外,我不知道这有多强大 - 我只是让 OP 的示例工作并停在那里.所以FWIW:


New method follows based on comment from OP. Unfortunately this method is a lot more complicated and uses private members. Also I don't know how robust this - I just got the OP's example to work and stopped there. So FWIW:

# wh is alias for write-host
# sel is alias for select-object

# The following two use reflection to access private members:
#   invoke-method invokes private methods
#   select-properties is similar to select-object, but it gets private properties

# Get the system.management.automation assembly
$smaa=[appdomain]::currentdomain.getassemblies()|
         ? location -like "*system.management.automation*"

# Get the StopUpstreamCommandsException class
$upcet=$smaa.gettypes()| ? name -like "*upstream*"

filter x {
  [CmdletBinding()]
  param(
    [parameter(ValueFromPipeline=$true)]
    [object] $inputObject
  )
  process {
    if ($inputObject -ge 5) {
      # Create a StopUpstreamCommandsException
      $upce = [activator]::CreateInstance($upcet,@($pscmdlet))

      $PipelineProcessor=$pscmdlet.CommandRuntime|select-properties PipelineProcessor
      $commands = $PipelineProcessor|select-properties commands
      $commandProcessor= $commands[0]

      $null = $upce.RequestingCommandProcessor|select-properties *

      $upce.RequestingCommandProcessor.commandinfo =  
          $commandProcessor|select-properties commandinfo

      $upce.RequestingCommandProcessor.Commandruntime =  
          $commandProcessor|select-properties commandruntime

      $null = $PipelineProcessor|
          invoke-method recordfailure @($upce, $commandProcessor.command)

      1..($commands.count-1) | % {
        $commands[$_] | invoke-method DoComplete
      }

      wh throwing
      throw $upce
    }
    wh "< $inputObject >"

    $inputObject
  } # end process
  end {
    wh in x end
  }
} # end filter x

filter y {
  [CmdletBinding()]
  param(
    [parameter(ValueFromPipeline=$true)]
    [object] $inputObject
  )
  process {
    $inputObject
  }
  end {
    wh in y end
  }
}

1..5| x | y | measure -Sum

通过反射检索 PipelineProcessor 值的 PowerShell 代码:

PowerShell code to retrieve PipelineProcessor value through reflection:

$t_cmdRun = $pscmdlet.CommandRuntime.gettype()
# Get pipelineprocessor value ($pipor)
$bindFlags = [Reflection.BindingFlags]"NonPublic,Instance"
$piporProp = $t_cmdRun.getproperty("PipelineProcessor", $bindFlags )
$pipor=$piporProp.GetValue($PSCmdlet.CommandRuntime,$null)

Powershell 代码通过反射调用方法:

Powershell code to invoke method through reflection:

$proc = (gps)[12] # semi-random process
$methinfo = $proc.gettype().getmethod("GetComIUnknown", $bindFlags)
# Return ComIUnknown as an IntPtr
$comIUnknown = $methinfo.Invoke($proc, @($true))

这篇关于Select-Object 如何在 PowerShell v3 中停止管道?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆