2012-01-05 4 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-stopupstreamcommandsex- 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에서 파이프 라인을 래핑해야한다는 것을 알 필요없이 파이프 라인을 중지 할 수 있기를 원하지만 private 유형이므로 StopUpstreamCommandsException을 사용할 수 없다고 가정합니다. – Rynant

1

나는 PipelineStoppedException를 던지고 파이프 라인을 멈출 것을 알고있다. 다음의 예는 2.0, 당신은 버전 3.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 | select-first 3 | measure'라고 부르지 만, 그렇지 않습니다 :'1..5 | 선택 - 개선 - 첫 번째 3 | measure' – Rynant

3

의 ExecutionContext를 StopUpstreamCommandsException, ActionPreferenceStopException 및 PipelineClosedException을 던지는 $ PSCmdlet.ThrowTerminatingError를 호출하고 $ 등 여러 가지 방법을 시도 후. stopper.set_IsStopping ($ true) 마지막으로 select-object를 사용하는 것만이 전체 스크립트를 중단하지 않는 유일한 방법이라는 것을 알게되었습니다 (파이프 라인과 비교). [위에서 언급 한 항목 중 일부는 내가 반사를 통해 액세스 private 멤버에 대한 액세스를 필요로합니다.]

# 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의 모범을 얻고 거기에서 멈추었습니다.그래서 FWIW : 반사를 통해 메소드를 호출

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

파워 쉘 코드 :

# 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 값을 검색합니다 그래서

$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 | 선택 - 첫 번째 3 | measure -Sum'은 결과를 반환하지만'1..5 | % {if ($ _ -ge 4) {stop-pipeline}} | 측정 -Sum'하지 않습니다. 새 항목이 파이프 라인을 통해 전송되는 것을 중지하고 파이프 라인이 처리를 완료하도록하고 싶습니다. – Rynant

+1

'invoke-method'와'select-properties' 함수에 대한 코드를 포함 할 수 있습니까? 제공된 코드는 해당 기능 없이는 작동하지 않습니다. – Rynant

관련 문제