2012-01-05 2 views
10

、次の行:Select-ObjectはPowerShell v3のパイプラインをどのように停止しますか? PowerShellのV2で

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

なる表示:すべての要素がパイプラインを押し下げたため

Value : 1 
1 
Value : 2 
Value : 3 

。しかし、V3上記線のみ表示:2及び3は、Foreach-Objectに送信される前に

Value : 1 
1 

パイプラインが停止している(注:Select-Objectため-Waitスイッチはすべての要素がforeachブロックに到達することを可能にします)。

Select-Objectはどのようにパイプラインを停止するのですか?foreachまたは自分の機能からパイプラインを停止できますか?

編集:私はパイプラインの外にループして続行しながら、私は...やるにおけるパイプラインをラップすることができます知っています。

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

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

しかしSelect-Objectはので、私はへの道があったことを期待していたこれらの技術のいずれかを必要としない:私はまた、v3の中で、私はこのような何かを(それがV2では動作しません)行うことができますことを発見しましたパイプライン内の単一ポイントからパイプラインを停止します。

+1

だから、StopUpstreamCommandsExceptionを探しているが、あなたのことができそれは内部であるので使用しないでください。ここには、それのためのMS接続の提案があります:https://connect.microsoft.com/PowerShell/feedback/details/768650/enable-users-to-stop-pipeline-making-stopupstreamcommandsexception-public –

+1

ありがとう、私を指している@LarsTruijensそれに。私はそれを投票した。 – Rynant

答えて

3

あなたがパイプラインをキャンセルすることができますどのようにこの記事をチェックしてください:PowerShellの3.0では
http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx

を、それはエンジンの改善です。 CTP1サンプルフォルダ( '\エンジンデモ\その他\ 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 
+0

はい、私はこれらの両方を見ました。私は私の質問を更新しました。 – Rynant

+0

私が言う限り、StopUpstreamCommandsException例外がスローされます。これは、Tobiasが私が言及したポストで行っていることと非常によく似ています。 –

+1

しかし、PipelineStoppedExceptionとは異なり、 'Select-Object'はダウンストリームコマンドの完了を妨げません。私はパイプラインを停止することができるようにするために、パイプラインをdo-whileまたはtry-catchでラップする必要があることをユーザーに知らせずに停止できるようにしたいと考えていますが、プライベート型なのでStopUpstreamCommandsExceptionは使用できません。 – Rynant

1

私はPipelineStoppedExceptionを投げることはパイプラインを停止することを知っています。次の例では、V2.0では、あなたがV3.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" 
+0

あなたは誤字があります:$ fist> $ first。そして、 '新しい'は新しいオブジェクトでなければなりません。 –

+0

@ShayLevy - ありがとう、訂正されました:) – manojlds

+1

PipelineStoppedExceptionをスローする際の問題は、パイプラインのさらに下のコマンドが処理を終了しないということです。これはうまくいきます: '1..5 |選択 - 第1の3 | 'measure'を返しますが、これは行いません:' 1..5 |選択 - 改善 - 第1の3 | measure' – Rynant

3

$ PSCmdlet.ThrowTerminatingErrorと$のExecutionContextを呼び出し、StopUpstreamCommandsException、ActionPreferenceStopException、およびPipelineClosedExceptionを投げるなど、いくつかの方法を、試した後。 stop.set_IsStopping($ true)私は最終的にselect-objectを利用することだけがスクリプト全体(パイプラインと比べて)を中止しなかった唯一のものであることを発見しました。 【上記項目のいくつかは、私は反射を介してアクセスプライベートメンバへのアクセスを必要とすることに留意されたい。】

# 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:リフレクションを通じてPipelineProcessor値を取得する

# 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のコード:反射によってメソッドを呼び出すための

$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)) 
+0

フォーマットを改善してください。 –

+0

これは私が探していることをしません。 '1..5 |を選択します。 measure -Sum'は結果を返しますが、 '1..5 | %{if($ _ -ge 4){停止パイプライン}} |測定-Sum'はしません。新しいアイテムがパイプラインを通じて送信されるのを止めたいが、パイプラインが処理を完了できるようにしたい。 – Rynant

+1

'invoke-method'と' select-properties'関数のコードを含めることができますか?これらの関数がなければ、提供されたコードは動作しません。 – Rynant

関連する問題