2012-01-05 4 views
10

In PowerShell v2, la seguente riga:In che modo Select-Object interrompe la pipeline in PowerShell v3?

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

mostrerà:

Value : 1 
1 
Value : 2 
Value : 3 

Dal momento che tutti gli elementi sono stati spinti giù la conduttura. Tuttavia, in v3 linea sopra mostra solo:

Value : 1 
1 

La conduttura viene arrestato prima 2 e 3 vengono inviati Foreach-Object (Nota: l'interruttore -Wait per Select-Object consente a tutti gli elementi di raggiungere il blocco foreach).

Come si Select-Object fermare il gasdotto, e possono ora fermare l'oleodotto da un foreach o dalla mia funzione?

Modifica: So che posso eseguire il wrapping di una pipeline in un do ... while loop e continuare fuori dalla pipeline. Ho anche scoperto che in v3 posso fare qualcosa di simile (non funziona in v2):

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

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

Ma Select-Object non richiede una di queste tecniche in modo speravo che ci fosse un modo per fermare la pipeline da un singolo punto della pipeline.

+1

Quindi siete alla ricerca di StopUpstreamCommandsException, ma è possibile lo uso poiché è interno Ecco un suggerimento per MS connect: https://connect.microsoft.com/PowerShell/feedback/details/768650/enable-users-to-stop-pipeline-making-stopupstreamcommandsexception-public –

+1

Grazie, @LarsTruijens per avermi indicato a tale; Ho votato. – Rynant

risposta

3

Controllare questo post su come si può annullare un oleodotto:
http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx

In PowerShell 3.0 è un miglioramento del motore. Dalla cartella campioni CTP1 ('\ Motori 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 
+0

Sì, ho visto entrambi. Ho aggiornato la mia domanda. – Rynant

+0

Per quanto posso dire getta un'eccezione StopUpstreamCommandsException, che è molto simile a ciò che Tobias sta facendo nel post che ho citato. –

+1

Ma a differenza di PipelineStoppedException, 'Select-Object' non impedisce il completamento dei comandi downstream. Vorrei poter interrompere la pipeline senza che l'utente sappia che deve eseguire il wrapping della pipeline in un do-while o try-catch, ma suppongo che non sia possibile utilizzare StopUpstreamCommandsException poiché si tratta di un tipo privato. – Rynant

1

so che lanciando un PipelineStoppedException ferma la pipeline. Il seguente esempio di simulare ciò che si vede con Select -first 1 in v3.0, v2.0 in:

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

Hai un errore di battitura: $ pugno> $ prima. E e 'nuovo' dovrebbe essere nuovo-oggetto. –

+0

@ShayLevy - Grazie, corretto :) – manojlds

+1

Il problema con il lancio di una PipelineStoppedException è che i comandi più avanti nella pipeline non terminano l'elaborazione. Funziona così: '1..5 | seleziona -prima 3 | misura', ma questo non: '1..5 | Select-Improved -first 3 | measure' – Rynant

3

Dopo aver provato diversi metodi, tra cui gettare StopUpstreamCommandsException, ActionPreferenceStopException, e PipelineClosedException, chiamando $ PSCmdlet.ThrowTerminatingError e $ ExecutionContext. Host.Runspace.GetCurrentlyRunningPipeline(). Stopper.set_IsStopping ($ true) Alla fine ho scoperto che il solo utilizzo di select-object era l'unica cosa che non ha annullato l'intero script (rispetto alla sola pipeline). [Si noti che alcuni degli elementi di cui sopra richiedono l'accesso ai membri privati, che ho consultati dall'utente tramite riflessione.]

# 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() 
} 

Nuovo metodo segue basa sul commento da OP. Sfortunatamente questo metodo è molto più complicato e utilizza membri privati. Inoltre non so quanto sia robusto questo - ho appena ottenuto l'esempio dell'OP per lavorare e mi sono fermato lì.Così FWIW: codice

# 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 per recuperare il valore PipelineProcessor attraverso la riflessione:

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

codice PowerShell per richiamare il metodo attraverso la riflessione:

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

Migliora la formattazione, per favore. –

+0

Questo non fa quello che sto cercando. '1..5 | selezionare -Primo 3 | measure -Sum' restituisce un risultato, ma '1..5 | % {if ($ _ -ge 4) {stop-pipeline}} | misura -Sum' no. Voglio interrompere l'invio di nuovi articoli attraverso la pipeline, ma consentire alla pipeline di completare l'elaborazione. – Rynant

+1

Puoi includere il codice per le funzioni 'invoke-method' e' select-properties'? Il codice fornito non funzionerà senza quelle funzioni. – Rynant