簡體   English   中英

選擇對象如何停止PowerShell v3中的管道?

[英]How does Select-Object stop the pipeline in PowerShell v3?

在PowerShell v2中,以下行:

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

將顯示:

Value : 1
1
Value : 2
Value : 3

由於所有元素均已下推。 但是,在v3中,上述行僅顯示:

Value : 1
1

在將2和3發送到Foreach-Object之前,管道已停止(注意: Select-Object-Wait開關允許所有元素到達foreach塊)。

Select-Object如何停止管道,現在可以從foreach或自己的函數中停止管道嗎?

編輯:我知道我可以將管道包裝在do ... while循環中,然后繼續執行管道。 我還發現在v3中我可以做這樣的事情(在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不需要這兩種技術,因此我希望有一種方法可以從管道中的單個點停止管道。

在嘗試了幾種方法(包括拋出StopUpstreamCommandsException,ActionPreferenceStopException和PipelineClosedException)之后,調用$ PSCmdlet.ThrowTerminateError和$ ExecutionContext.Host.Runspace.GetCurrentlyRunningPipeline()。stopper.set_IsStopping($ true),我終於發現僅利用選擇對象是唯一的方法不會使整個腳本中止的事情(相對於管道而言)。 [請注意,上述某些項目需要訪問私有成員,我通過反射訪問了該成員。]

# 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:

# 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

PowerShell代碼通過反射檢索PipelineProcessor值:

$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代碼:

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

查看此帖子,了解如何取消管道:
http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx

在PowerShell 3.0中,它是引擎的改進。 從CTP1示例文件夾('\\ Engines Demos \\ Misc \\ ConnectBugFixes.ps1'):

# Connect Bug 332685
# Select-Object optimization
# Submitted by Shay Levi
# Connect Suggestion 286219
# PSV2: Lazy pipeline - ability for cmdlets to say "NO MORE"
# Submitted by Karl Prosser

# Stop the pipeline once the objects have been selected
# Useful for commands that return a lot of objects, like dealing with the event log

# In PS 2.0, this took a long time even though we only wanted the first 10 events
Start-Process powershell.exe -Args '-Version 2 -NoExit -Command Get-WinEvent | Select-Object -First 10'

# In PS 3.0, the pipeline stops after retrieving the first 10 objects
Get-WinEvent | Select-Object -First 10

我知道拋出PipelineStoppedException會停止管道。 以下示例將模擬您在v2.0,v2.0中使用Select -first 1看到的內容:

filter Select-Improved($first) {
    begin{
        $count = 0
    }
    process{
        $_
        $count++
        if($count -ge $first){throw (new-object System.Management.Automation.PipelineStoppedException)}
    }
}

trap{continue}
1..3| foreach { Write-Host "Value : $_"; $_ }| Select-Improved -first 1
write-host "after"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM