2008-11-10 9 views
7

He escrito un filtro simple de PowerShell que empuja el objeto actual hacia la tubería si su fecha se encuentra entre la fecha de inicio y la de finalización especificadas. Los objetos que salen de la tubería siempre están en orden de fecha ascendente, así que tan pronto como la fecha exceda la fecha final especificada, sé que mi trabajo está hecho y me gustaría decir a la tubería que los comandos ascendentes pueden abandonar su trabajo para que la tubería puede terminar su trabajo. Estoy leyendo algunos archivos de registro muy grandes y con frecuencia quiero examinar solo una parte del registro. Estoy bastante seguro de que esto no es posible, pero quería preguntar para estar seguro.¿Es posible terminar o detener una tubería de PowerShell desde un filtro?

Respuesta

3

No es posible detener un comando en sentido ascendente desde un comando en sentido descendente .. continuará filtrando los objetos que no coinciden con sus criterios, pero el primer comando procesará todo lo que se configuró para procesar.

La solución alternativa consistirá en filtrar más en el cmdlet o la función/filtro en sentido ascendente. Trabajar con archivos de registro lo hace un poco más complicado, pero tal vez usar Select-String y una expresión regular para filtrar las fechas no deseadas podrían funcionar para usted.

A menos que sepa cuántas líneas desea tomar y desde dónde, se leerá todo el archivo para verificar el patrón.

1

Pruebe estos filtros, forzarán a la tubería a detenerse después del primer objeto -o los primeros n elementos- y lo almacenan -ellos- en una variable; debe pasar el nombre de la variable, si no lo hace, los objetos se expulsan pero no se pueden asignar a una variable.

filter FirstObject ([string]$vName = '') { 
if ($vName) {sv $vName $_ -s 1} else {$_} 
break 
} 

filter FirstElements ([int]$max = 2, [string]$vName = '') { 
if ($max -le 0) {break} else {$_arr += ,$_} 
if (!--$max) { 
    if ($vName) {sv $vName $_arr -s 1} else {$_arr} 
    break 
} 
} 

# can't assign to a variable directly 
$myLog = get-eventLog security | ... | firstObject 

# pass the the varName 
get-eventLog security | ... | firstObject myLog 
$myLog 

# can't assign to a variable directly 
$myLogs = get-eventLog security | ... | firstElements 3 

# pass the number of elements and the varName 
get-eventLog security | ... | firstElements 3 myLogs 
$myLogs 

#################################### 

get-eventLog security | % { 
if ($_.timegenerated -lt (date 11.09.08) -and` 
    $_.timegenerated -gt (date 11.01.08)) {$log1 = $_; break} 
} 

# 
$log1 
+2

Esto no funciona en un bucle - la ruptura parece detener el bucle demasiado –

+0

@DavidGardiner: En efecto: '' break' y continue' se _No_ diseñado para salir tuberías, salen de _loops_ (y 'switch' branches). Si no hay un bucle de delimitación, lo único que sucede es que la función se cierra, lo que, como efecto secundario, finaliza la interconexión. Sin embargo, si hay un bucle delimitador, 'break' y' continue' actúan en consecuencia, lo que puede ser inesperado. Una solución alternativa simple pero frágil e inconveniente es envolver la invocación en un bucle ficticio, como en [@ MaximumCookie's answer] (http://stackoverflow.com/a/30943992/45375). – mklement0

2
No

seguro acerca de sus necesidades exactas, pero puede ser digno de su tiempo para mirar Log Parser para ver si usted no puede utilizar una consulta para filtrar los datos antes de que incluso golpea la tubería.

0

Otra opción sería usar el parámetro -file en una declaración switch. Usando -file leerá el archivo una línea a la vez, y puede usar break para salir inmediatamente sin leer el resto del archivo.

switch -file $someFile { 
    # Parse current line for later matches. 
    { $script:line = [DateTime]$_ } { } 
    # If less than min date, keep looking. 
    { $line -lt $minDate } { Write-Host "skipping: $line"; continue } 
    # If greater than max date, stop checking. 
    { $line -gt $maxDate } { Write-Host "stopping: $line"; break } 
    # Otherwise, date is between min and max. 
    default { Write-Host "match: $line" } 
} 
4

Es posible romper una tubería con cualquier cosa que de otro modo romper un bucle exterior o la ejecución del script alto en conjunto (como lanzar una excepción). La solución entonces es envolver la tubería en un bucle que puede romper si necesita detener la tubería.Por ejemplo, el código de abajo devolverá el primer punto de la tubería y luego romper la tubería al romper el bucle exterior do-while:

do { 
    Get-ChildItem|% { $_;break } 
} while ($false) 

Esta funcionalidad se puede envolver en una función como esta, en la última línea consigue el mismo efecto que el anterior:

function Breakable-Pipeline([ScriptBlock]$ScriptBlock) { 
    do { 
     . $ScriptBlock 
    } while ($false) 
} 

Breakable-Pipeline { Get-ChildItem|% { $_;break } } 
0

He aquí un - imperfecto - la aplicación de un cmdlet Stop-Pipeline (requiere PS + v3), adaptado de agradecimiento this answer:

#requires -version 3 
Filter Stop-Pipeline { 
    $sp = { Select-Object -First 1 }.GetSteppablePipeline($MyInvocation.CommandOrigin) 
    $sp.Begin($true) 
    $sp.Process(0) 
} 

# Example 
1..5 | % { if ($_ -gt 2) { Stop-Pipeline }; $_ } # -> 1, 2 

Advertencia: No entiendo completamente cómo funciona, aunque fundamentalmente aprovecha la capacidad de Select -First de detener la tubería prematuramente (PS v3 +). Sin embargo, en este caso hay una diferencia crucial para Select -First cómo termina la tubería: aguas abajo cmdlets (comandos más adelante en la tubería) no tienen la oportunidad de ejecutar sus end bloques.
Por lo tanto, cmdlets agregantes (aquellos que deben recibir todo de entrada antes de la producción de salida, como Sort-Object, Group-Object, y Measure-Object) no producirá salida si se coloca más tarde en la misma tubería; ej .:

# !! NO output, because Sort-Object never finishes. 
1..5 | % { if ($_ -gt 2) { Stop-Pipeline }; $_ } | Sort-Object 

información de fondo que pueden conducir a una mejor solución:

Gracias a PetSerAl, mi answer here muestra cómo producir la misma excepción que Select-Object -First utiliza internamente para dejar de cmdlets de aguas arriba.

Sin embargo, existe la excepción se produce desde el interior del cmdlet que es sí mismo conectado a la tubería para detener, que no es el caso aquí:

Stop-Pipeline, como se usa en los ejemplos anteriores, no es conectado a la tubería que debe detenerse (solo se incluye el bloque ForEach-Object()), entonces la pregunta es: ¿cómo se puede lanzar la excepción en el contexto de la canalización objetivo?

+0

Consulte mi respuesta para obtener una forma de hacerlo utilizando miembros no públicos. –

1

Si está dispuesto a usar miembros no públicos aquí, hay una manera de detener la tubería. Imita lo que hace select-object. invoke-method (alias im) es una función para invocar métodos no públicos. select-property (alias selp) es una función para seleccionar (similar a seleccionar el objeto) propiedades no públicas; sin embargo, actúa automáticamente como -ExpandProperty si solo se encuentra una propiedad coincidente. (Escribí select-property y invoke-method en el trabajo, por lo que no puedo compartir el código fuente de esos).

# Get the system.management.automation assembly 
$script:smaa=[appdomain]::currentdomain.getassemblies()| 
     ? location -like "*system.management.automation*" 
# Get the StopUpstreamCommandsException class 
$script:upcet=$smaa.gettypes()| ? name -like "*StopUpstreamCommandsException *" 

function stop-pipeline { 
    # Create a StopUpstreamCommandsException 
    $upce = [activator]::CreateInstance($upcet,@($pscmdlet)) 

    $PipelineProcessor=$pscmdlet.CommandRuntime|select-property PipelineProcessor 
    $commands = $PipelineProcessor|select-property commands 
    $commandProcessor= $commands[0] 

    $ci = $commandProcessor|select-property commandinfo 
    $upce.RequestingCommandProcessor | im set_commandinfo @($ci) 

    $cr = $commandProcessor|select-property commandruntime 
    $upce.RequestingCommandProcessor| im set_commandruntime @($cr) 

    $null = $PipelineProcessor| 
     invoke-method recordfailure @($upce, $commandProcessor.command) 

    if ($commands.count -gt 1) { 
     $doCompletes = @() 
     1..($commands.count-1) | % { 
     write-debug "Stop-pipeline: added DoComplete for $($commands[$_])" 
     $doCompletes += $commands[$_] | invoke-method DoComplete -returnClosure 
     } 
     foreach ($DoComplete in $doCompletes) { 
     $null = & $DoComplete 
     } 
    } 

    throw $upce 
} 

EDIT: por el comentario de mklement0:

Aquí está una link al blog de tinta Nivot en un guión en el módulo "poke" que de igual forma se accede a los miembros que no son públicas.

En cuanto a los comentarios adicionales, no tengo los significativos en este momento. Este código simplemente imita lo que revela una descompilación de select-object. Los comentarios originales de MS (si los hay) no están en la descompilación. Francamente, no sé el propósito de los diversos tipos que utiliza la función. Obtener ese nivel de comprensión probablemente requerirá una cantidad considerable de esfuerzo.

Mi sugerencia: obtener el módulo de inserción de Oisin. Ajusta el código para usar ese módulo. Y luego pruébalo. Si te gusta la forma en que funciona, úsala y no te preocupes por cómo funciona (eso es lo que hice).

Nota: No he estudiado "poke" en profundidad, pero supongo que no tiene nada como -returnClosure. Sin embargo añadió que debería ser de fácil:

if (-not $returnClosure) { 
    $methodInfo.Invoke($arguments) 
} else { 
    {$methodInfo.Invoke($arguments)}.GetNewClosure() 
} 
+0

Impresionante, pero alucinante. Si está preparado, quizás pueda agregar más comentarios y también señalar cómo otros podrían implementar sus propias versiones de 'select-property' y' invoke-method'. – mklement0

Cuestiones relacionadas