2012-01-05 11 views
10

En PowerShell v2, la siguiente línea:¿Cómo detiene Select-Object la canalización en PowerShell v3?

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

pantalla Would:

Value : 1 
1 
Value : 2 
Value : 3 

Dado que todos los elementos fueron empujados por la tubería. Sin embargo, en v3 la línea anterior sólo muestra:

Value : 1 
1 

La tubería se detiene antes de 2 y 3 son enviados a Foreach-Object (Nota: el interruptor de -Wait para Select-Object permite que todos los elementos para alcanzar el bloque foreach).

¿Cómo detiene Select-Object la tubería, y ahora puedo detener la tubería desde un foreach o desde mi propia función?

Editar: Sé que puedo envolver una tubería en un bucle do ... while y continuar fuera de la tubería. También he encontrado que en la versión 3 que puedo hacer algo como esto (que no funciona en la versión 2):

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

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

Pero Select-Object no requiere cualquiera de estas técnicas así que esperaba que hubiera una manera de detener la tubería desde un solo punto en la tubería.

+1

por lo que busca StopUpstreamCommandsException, pero se puede no lo uso ya que es interno. Aquí hay una sugerencia de MS connect para ello: https://connect.microsoft.com/PowerShell/feedback/details/768650/enable-users-to-stop-pipeline-making-stopupstreamcommandsexception-public –

+1

Gracias, @LarsTruijens por señalarme a ese; Lo voté. – Rynant

Respuesta

3

Comprobar este post sobre cómo puede cancelar una tubería:
http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx

En PowerShell 3.0 es una mejora del motor. En la carpeta CTP1 muestras ('\ 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 
+0

Sí, he visto ambos. He actualizado mi pregunta. – Rynant

+0

Por lo que puedo decir, arroja una excepción StopUpstreamCommandsException, que es muy similar a lo que Tobias está haciendo en la publicación que mencioné. –

+1

Pero, a diferencia de PipelineStoppedException, 'Select-Object' no impide que los comandos en sentido descendente se completen. Me gustaría poder detener la interconexión sin requerir que el usuario sepa que tienen que completar el pipeline en un do-while o try-catch, pero supongo que no se puede usar StopUpstreamCommandsException ya que es un tipo privado. – Rynant

1

Sé que lanzar una PipelineStoppedException detiene la tubería. El siguiente ejemplo se simulará lo que ves con Select -first 1 en la versión 3.0, en v2.0:

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

Tiene un error tipográfico: $ puño> $ primero. Y y 'nuevo' debería ser un nuevo objeto. –

+0

@ShayLevy - Gracias, corregido :) – manojlds

+1

El problema que tengo al arrojar una PipelineStoppedException es que los comandos que se encuentran más abajo en la tubería no terminan el procesamiento. Esto funciona: '1..5 | seleccione -primero 3 | medida', pero esto no: '1..5 | Select-Improved -first 3 | medida' – Rynant

3

Después de probar varios métodos, incluyendo tirar StopUpstreamCommandsException, ActionPreferenceStopException y PipelineClosedException, llamando a $ y $ PSCmdlet.ThrowTerminatingError ExecutionContext. Host.Runspace.GetCurrentlyRunningPipeline(). Stopper.set_IsStopping ($ true) Finalmente encontré que solo utilizar select-object era lo único que no abortaba todo el script (frente a solo el pipeline). [Nótese que algunos de los puntos mencionados anteriormente requieren el acceso a los miembros privados, que yo accede a través de la reflexión.]

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

Nuevo método sigue basa en el comentario de la OP. Lamentablemente, este método es mucho más complicado y utiliza miembros privados. Además, no sé cuán robusto es esto, acabo de hacer que el ejemplo del OP funcione y me detuve allí.Así Fwiw: Código

# 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 para recuperar el valor PipelineProcessor través de la reflexión:

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

código Powershell para invocar el método mediante la reflexión:

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

Mejore el formateo, por favor. –

+0

Esto no hace lo que estoy buscando. '1..5 | seleccione -First 3 | measure -Sum' devuelve un resultado, pero '1..5 | % {if ($ _ -ge 4) {stop-pipeline}} | medida -Sum' no. Quiero evitar que se envíen nuevos artículos a través de la tubería, pero permitir que la tubería termine de procesarse. – Rynant

+1

¿Puede incluir el código para las funciones 'invoke-method' y' select-properties'? El código proporcionado no funcionará sin esas funciones. – Rynant

Cuestiones relacionadas