2008-11-10 13 views
7

Ich habe einen einfachen PowerShell-Filter geschrieben, der das aktuelle Objekt in die Pipeline verschiebt, wenn das Datum zwischen dem angegebenen Anfangs- und Enddatum liegt. Die Objekte, die sich in der Pipeline befinden, sind immer in aufsteigender Reihenfolge, sobald das Datum das angegebene Enddatum überschreitet. Ich weiß, dass meine Arbeit erledigt ist und ich möchte der Pipeline mitteilen, dass die Upstream-Befehle ihre Arbeit aufgeben können, damit die Pipeline kann seine Arbeit beenden. Ich lese einige sehr große Protokolldateien, und ich werde häufig nur einen Teil des Protokolls untersuchen. Ich bin mir ziemlich sicher, dass das nicht möglich ist, aber ich wollte sicher sein.Ist es möglich, eine PowerShell-Pipeline innerhalb eines Filters zu beenden oder zu stoppen?

Antwort

3

Es ist nicht möglich, einen Upstream-Befehl von einem Downstream-Befehl zu stoppen. Er wird weiterhin Objekte ausfiltern, die nicht Ihren Kriterien entsprechen, aber der erste Befehl wird alles verarbeiten, was er verarbeiten soll.

Die Problemumgehung wird mehr Filtern auf dem Upstream-Cmdlet oder Funktion/Filter sein. Die Arbeit mit Log-Dateien macht es etwas komplizierter, aber vielleicht kann die Verwendung von Select-String und ein regulärer Ausdruck zum Ausfiltern der unerwünschten Daten für Sie funktionieren.

Wenn Sie nicht wissen, wie viele Zeilen Sie verwenden möchten und von wo, wird die gesamte Datei gelesen, um nach dem Muster zu suchen.

1

Probieren Sie diese Filter aus, sie zwingen die Pipeline, nach dem ersten Objekt - oder den ersten n Elementen - anzuhalten und speichern sie -them- in einer Variablen; Sie müssen den Namen der Variablen übergeben, wenn das Objekt nicht ausgeschoben ist, aber keiner Variablen zugewiesen werden kann.

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

Dies gilt nicht in einer Schleife arbeiten - der Bruch scheint die Schleife zu –

+0

@DavidGardiner zu stoppen: In der Tat: 'break' und' CONTINUE ist _nicht_ entworfen, um zu beenden Pipelines, brechen sie aus _loops_ (und 'switch'-Zweigen). Wenn es keine umschließende Schleife gibt, passiert nur, dass die Funktion beendet wird - was als Nebeneffekt die Pipeline beendet. Wenn es jedoch eine umschließende Schleife gibt, dann wirken "break" und "continue" entsprechend, was unerwartet sein kann. Eine einfache, aber fragile und unbequeme Lösung besteht darin, den Aufruf in eine Dummy-Schleife umzuwandeln, wie in [@ MaximumCookies Antwort] (http://stackoverflow.com/a/30943992/45375). – mklement0

2

Nicht sicher über Ihre genauen Bedürfnisse, aber es kann Ihre Zeit wert sein, auf Log Parser zu schauen, um zu sehen, wenn Sie nicht eine Abfrage verwenden können, um die Daten zu filtern, bevor es sogar das Rohr trifft.

0

Eine andere Option wäre, den -file Parameter auf einer switch Anweisung zu verwenden. Mit -file wird die Datei Zeile für Zeile gelesen, und Sie können break verwenden, um sofort zu beenden, ohne den Rest der Datei zu lesen.

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 ist möglich, eine Pipeline mit etwas zu brechen, die sonst eine externe Schleife oder halt Skriptausführung brechen insgesamt (wie eine Ausnahme werfen). Die Lösung besteht dann darin, die Pipeline in eine Schleife zu packen, die Sie unterbrechen können, wenn Sie die Pipeline anhalten müssen.Zum Beispiel wird der Code unten auf das erste Element aus der Pipeline zurückkehrt und dann die Pipeline durchbrechen, indem die Außen do-while-Schleife zu brechen:

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

Diese Funktionalität kann in eine Funktion wie diese gewickelt wird, wo die letzte Zeile wie oben dasselbe erreicht:

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

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

Hier ist eine - unvollständige - Implementierung eines Stop-Pipeline Cmdlets (erfordert PS v3 +), dankbar angepasst von 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 

Vorbehalt: Ich verstehe nicht ganz, wie es funktioniert, obwohl es im Wesentlichen Select -First die Fähigkeit nutzt, die Pipeline vorzeitig zu beenden (PS v3 +). Es gibt jedoch in diesem Fall ist ein entscheidender Unterschied, wie Select -First beendet die Pipeline: Downstream Cmdlets (Befehle später in der Pipeline) nicht bekommen eine Chance, ihre end Blöcke zu laufen.
Daher Aggregieren cmdlets (jene, die all Eingang vor Ausgabe erzeugt, wie Sort-Object, Group-Object und Measure-Object erhalten müssen) produzieren wird nicht ausgegeben in der gleichen Pipeline später, wenn gesetzt; z.B .:

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

Hintergrundinformationen, die zu einer besseren Lösung führen:

Dank PetSerAl, mein answer here zeigen, wie die gleiche Ausnahme zu erzeugen, dass Select-Object -First intern verwendet Upstream-Cmdlets zu stoppen.

Es gibt jedoch eine Ausnahme von der Innenseite des cmdlet geworfen wird, das selbst an die Pipeline angeschlossen ist zu stoppen, was hier nicht der Fall ist:

Stop-Pipeline, wie oben in den Beispielen verwendet wird, ist nicht verbunden mit der Pipeline, die gestoppt werden sollte (nur der umschließende (%) Block ist), so ist die Frage: Wie kann die Ausnahme im Kontext der Zielpipeline geworfen werden?

+0

Siehe meine Antwort für einen Weg, dies mit nicht-öffentlichen Mitgliedern zu tun. –

1

Wenn Sie bereit sind, nicht-öffentliche Mitglieder zu verwenden, können Sie hier die Pipeline stoppen. Es imitiert was select-object tut. invoke-method (Alias ​​im) ist eine Funktion zum Aufrufen nichtöffentlicher Methoden. select-property (Alias ​​selp) ist eine Funktion zum Auswählen (ähnlich wie Select-Objekt) nicht-öffentliche Eigenschaften - aber es verhält sich automatisch wie -ExpandProperty, wenn nur eine passende Eigenschaft gefunden wird. (Ich schrieb select-property und invoke-method bei der Arbeit, so kann der Quellcode dieser nicht teilen).

# 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: pro Kommentar des mklement0:

ist hier ein link zum Nivot Tinte Blog auf einem Skript auf dem "Poke" Modul, das nicht-öffentliche Mitglieder gibt in ähnlicher Weise den Zugang zu.

Soweit zusätzliche Kommentare, habe ich an dieser Stelle keine aussagekräftigen. Dieser Code ahmt nur nach, was eine Dekompilierung von select-object zeigt. Die ursprünglichen MS-Kommentare (falls vorhanden) sind natürlich nicht in der Dekompilierung. Offen gesagt, ich kenne den Zweck der verschiedenen Typen, die die Funktion verwendet, nicht. Um dieses Verständnisniveau zu erreichen, wäre wahrscheinlich ein erheblicher Aufwand erforderlich.

Mein Vorschlag: Holen Oisin Poke-Modul. Ändern Sie den Code, um das Modul zu verwenden. Und dann probier es aus. Wenn Ihnen die Funktionsweise gefällt, dann verwenden Sie sie und machen Sie sich keine Sorgen, wie es funktioniert (das habe ich getan).

Hinweis: Ich habe "Poke" in keiner Tiefe studiert, aber meine Vermutung ist, dass es nichts wie -returnClosure hat. Allerdings fügt hinzu, dass, da dies einfach sein sollte:

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

Beeindruckend, aber umwerfend. Wenn Sie dafür bereit sind, können Sie vielleicht weitere Kommentare hinzufügen und darauf hinweisen, wie andere ihre eigenen Versionen von 'select-property' und' invoke-method' implementieren könnten. – mklement0