2013-05-20 2 views
5

Kurze Frage: hat jemand detaillierte Informationen zu -RemainingScripts Parameter von ForEach-Object?PowerShell: der mysteriöse -RemainingScripts-Parameter von ForEach-Objekt

Lange Frage:

Ich habe gerade angefangen Powershell aus der vergangenen Woche zu lernen und ich bin durch die einzelnen Cmdlets gehen, um mehr Details zu erfahren. Basierend auf öffentliche Dokumentation, wissen wir ForEach-Object Begin-Prozess-End haben Blöcke, wie folgt aus:

Get-ChildItem | foreach -Begin { "block1"; 
    $fileCount = $directoryCount = 0} -Process { "block2"; 
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}} -End { 
    "block3"; "$directoryCount directories and $fileCount files"} 

Ergebnis erwartet wird: 1 mal für "block1" und "Block3", "block2" wird wiederholt für Jedes übergebene Element und die Anzahl/Dateianzahl ist korrekt. So weit, ist es gut.

Nun, was ist interessant, ist der folgende Befehl funktioniert auch und gibt genau das gleiche Ergebnis:

Get-ChildItem | foreach { "block1" 
    $fileCount = $directoryCount = 0}{ "block2"; 
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}{ 
    "block3"; "$directoryCount directories and $fileCount files"} 

Nur 3 ScriptBlocks bestanden foreach. Basierend auf dem Handbuch geht der erste zu Prozess (Position 1). Aber wie steht es mit den restlichen 2? Laut Handbuch gibt es keinen Parameter mit "Position 2". Also wandte ich mich Trace-Command zu und stellte fest, dass die späteren 2 Skriptblöcke eigentlich zu RemainingScripts als "IList with 2 elements" gehörten.

BIND arg [$fileCount = $directoryCount = 0] to parameter [Process] 
BIND arg [System.Management.Automation.ScriptBlock[]] to param [Process] SUCCESSFUL 
BIND arg [System.Collections.ArrayList] to parameter [RemainingScripts] 
BIND arg [System.Management.Automation.ScriptBlock[]] to param [RemainingScripts] SUCCESSFUL 

Also, wenn ich den Befehl dazu ändern:

# No difference with/without the comma "," between the last 2 blocks 
Get-ChildItem | foreach -Process { "block1" 
    $fileCount = $directoryCount = 0} -RemainingScripts { "block2"; 
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}},{ 
    "block3"; "$directoryCount directories and $fileCount files"} 

Dennoch genau das gleiche Ergebnis.

So wie Sie bemerkt haben, geben alle 3 Befehle das gleiche Ergebnis. Dies wirft die interessante Frage auf: Beide der beiden letzten Befehle (implizit) angegeben -Process, jedoch ForEach-Object endet überraschend mit dem Argument von -Process als "-Begin"! (Skriptblock wird einmal am Anfang ausgeführt).

Dieses Experiment legt nahe:

  1. -RemainingScripts Parameter alle ungebundenen ScriptBlocks nehmen
  2. Wenn drei Blöcke in geben werden, obwohl die erste, -Prozess geht es später tatsächlich als „Begin“ verwendet wird, während die restlichen 2 werden "Prozess" und "Ende"

Dennoch sind alle oben nur meine wilde Vermutung. Ich habe keine Dokumentation gefunden, die meine Vermutung stützt

So, endlich gehen wir zurück zu meiner kurzen Frage :) Jeder hat detaillierte Informationen zu -RemainingScripts Parameter von ForEach-Object?

Danke.

Antwort

3

ich mehr Forschung haben und nun darauf vertrauen, um das Verhalten von -RemainingScripts Parameter zu beantworten, wenn mehrere ScriptBlocks in geben werden.

Wenn Sie die folgenden Befehle ausführen und überprüfen Sie das Ergebnis sorgfältig, Sie werden das Muster finden. Es ist nicht ganz einfach, aber immer noch nicht schwer zu verstehen.

1..5 | foreach { "process block" } { "remain block" } 
1..5 | foreach { "remain block" } -Process { "process block" } 
1..5 | foreach { "remain block" } -End { "end block" } -Process { "process block" } -Begin { "begin block" } 
1..5 | foreach { "remain block 1" } -End { "end block" } -Process { "process block" } { "remain block 2" } 
1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } -Begin { "begin block" } 
1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } { "remain block 3" } 
1..5 | foreach { "process block" } { "remain block 1" } { "remain block 2" } -Begin { "begin block" } 
1..5 | foreach { "process block" } { "remain block 1" } { "remain block 2" } { "remain block 3" } 

Also, was ist das Muster hier?

  • Wenn einzelne Script ist übergeben: einfach, es geht nur um -Prozess (die häufigste Nutzung)

  • Wann genau 2 ScriptBlocks in vergangen sind, gibt es drei mögliche Kombinationen

    1. -Prozess & -BEGIN -> als
    2. angegeben ausführen
    3. -Prozess & -Ende -> als
    4. angegeben ausführen
    5. -Prozess & -RemainingScripts -> Prozess wird beginnen, während RemainingScripts Prozess wird

Wenn wir diese zwei Anweisungen ausführen:

1..5 | foreach { "process block" } { "remain block" } 
1..5 | foreach { "remain block" } -Process { "process block" } 

# Both of them will return: 
process block 
remain block 
remain block 
remain block 
remain block 
remain block 

Wie werden Sie feststellen, dies ist nur eine besondere Bei folgendem Testfall:

  • Wenn mehr als 2 ScriptBlocks übergeben werden, folgen Sie dieser Workflow:

    1. Alle Skriptblöcke wie angegeben binden (Begin, Process, End); restliche ScriptBlocks gehen zu RemainingScripts
    2. Alle Skripte bestellen als: Begin> Prozess> Verbleibende> Ende
    3. Ergebnis der Bestellung ist eine Sammlung von ScriptBlocks. Lassen Sie uns diese Sammlung nennen OrderedScriptBlocks

      • Wenn Beginn/Ende nicht gebunden sind, einfach ignorieren
    4. (intern) Re-bind Parameter basierend auf OrderedScriptBlocks

      • OrderedScriptBlocks [ 0] wird Anfang
      • OrderedScriptBlocks [1 ..-2] geworden Verfahren
      • OrderedScriptBlocks [-1] (die letzte) wird Ende

Lassen Sie uns dieses Beispiel nehmen

1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } { "remain block 3" } 

bestellen Ergebnis:

{ "process block" } # new Begin 
{ "remain block 1" } # new Process 
{ "remain block 2" } # new Process 
{ "remain block 3" } # new End 

Jetzt ist das Ergebnis der Ausführung vollständig vorhersehbar:

process block 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 3 

, dass das Geheimnis hinter -RemainingScripts ist und jetzt verstehen wir, mehr internes Verhalten von ForEach-Object!

Noch muss ich zugeben, dass es keine Dokumentation gibt, die meine Vermutung stützt (nicht meine Schuld!), Aber diese Testfälle sollten ausreichen, um das beschriebene Verhalten zu erklären.

1

Hier sind die Details. ValueFromRemainingArguments ist auf "True" gesetzt, damit Ihre Vermutung korrekt ist.

help ForEach-Object 

-RemainingScripts <ScriptBlock[]> 
    Takes all script blocks that are not taken by the Process parameter. 

    This parameter is introduced in Windows PowerShell 3.0. 

gcm ForEach-Object | select -exp parametersets 

Parameter Name: RemainingScripts 
    ParameterType = System.Management.Automation.ScriptBlock[] 
    Position = -2147483648 
    IsMandatory = False 
    IsDynamic = False 
    HelpMessage = 
    ValueFromPipeline = False 
    ValueFromPipelineByPropertyName = False 
    ValueFromRemainingArguments = True 
    Aliases = {} 
    Attributes = 
    System.Management.Automation.ParameterAttribute 
    System.Management.Automation.AllowEmptyCollectionAttribute 
    System.Management.Automation.AllowNullAttribute 
+0

Dank Andy, haben Sie Punkt 1 beantwortet: "-RemainingScripts Parameter wird alle ungebundenen ScriptBlocks nehmen", aber nicht Punkt # 2 "Wenn 3 Blöcke übergeben werden, obwohl der erste geht zu -Process ... "Das ist eigentlich der Hauptgrund, warum diese Frage gestellt wird. Ich habe mehr Nachforschungen angestellt, und jetzt denke ich, dass ich # Punkt 2 beantworten kann. Trotzdem schätze ich Ihre Antwort und die Technik zur Überprüfung der Parameter. Und tut mir leid, ich habe keine Erlaubnis, für deine Antwort zu stimmen, obwohl es sehr nützlich ist. –

Verwandte Themen