2013-05-20 9 views
5

Krótkie pytanie: ktoś ma szczegółowe informacje na temat parametru -RemainingScripts z ForEach-Object?PowerShell: tajemniczy parametr -ReainingScripts dla ForEach-Object

Długie pytanie:

Właśnie rozpoczął naukę PowerShell z ostatniego tygodnia i jadę przez każdy apletów poleceń, aby dowiedzieć się więcej szczegółów. Na podstawie dokumentacji publicznej, wiemy ForEach-Object może mieć Begin-Process-End bloków, jak to: Oczekuje

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

Wynik: 1 godzina dla „blok1” i „blok3”, „blok2” jest powtarzany dla każdy element przekazany, a liczba sztuk/plików jest poprawna. Jak na razie dobrze.

Teraz, co ciekawe, następujące polecenie działa również i daje dokładnie ten sam wynik:

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

zaledwie 3 ScriptBlocks przekazane do foreach. Bazując na instrukcji, pierwszy przechodzi do -Procesu (Pozycja 1). Ale co z pozostałymi 2? Zgodnie z instrukcją, nie ma parametru "Pozycja 2". Tak więc zwróciłem się do Trace-Command i odkryłem, że późniejsze 2 bloki skryptów były w rzeczywistości RemainingScripts jako "IList z 2 elementami".

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 

Więc jeśli mogę zmienić polecenie, aby w ten sposób:

# 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"} 

wciąż dokładnie taki sam rezultat.

Tak jak zauważyłeś, wszystkie 3 polecenia dają ten sam rezultat. Podnosi to interesujące pytanie: obie późniejsze dwie komendy (implicite) określone -Proces, jednak ForEach-Object zaskakująco kończy się używanie argumentu -Process jako "-Begin"! (blok skryptu jest wykonywany raz na początku).

Ten eksperyment sugeruje:

  1. -RemainingScripts parametr podejmie wszelkie niezwiązane ScriptBlocks
  2. Po 3 bloki są przekazywane w, jednak pierwszy z nich idzie do -Process, później to jest faktycznie wykorzystywany jako „Początek” podczas gdy pozostałe 2 stają się "Proces" i "Koniec"

Nadal wszystkie powyższe są tylko moje dzikie domysły. Nie znalazłem dokumentacji potwierdzającej moje przypuszczenie.

W końcu wracamy do mojego krótkiego pytania :) Ktoś ma szczegółowe informacje o parametrze -RemainingScripts z ForEach-Object?

Dzięki.

Odpowiedz

3

zrobiłem więcej badań, a teraz czują się pewnie, by odpowiedzieć na zachowanie parametru -RemainingScripts gdy wiele ScriptBlocks zapadają w.

Jeśli uruchom następujące polecenia i sprawdzić wynik uważnie, znajdziesz wzór. To nie jest proste, ale nadal nie jest trudne do wymyślenia.

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" } 

Więc jaki jest wzór tutaj?

  • Kiedy pojawia się pojedynczy ScriptBlock przeszedł w: łatwa, po prostu idzie do -Process (najbardziej powszechne użycie)

  • Kiedy dokładnie 2 ScriptBlocks zapadają się, są 3 możliwe kombinacje

    1. -Process & -Begin -> wykonać określone
    2. -Process & końcem -> wykonać określone
    3. -Process & -RemainingScripts -> Proces staje się rozpocząć, gdy RemainingScripts staje procesowi

Jeśli prowadzimy te 2 wypowiedzi:

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 

Jak można dowiedzieć się, jest to po prostu specjalny w przypadku następującego przypadku testowego:

  • Po przekazaniu więcej niż 2 ScriptBlocks, wykonaj ten przepływ pracy:

    1. Powiąż wszystkie blokady skryptów zgodnie z podanymi ustawieniami (Rozpocznij, Przetwórz, Zakończ); pozostałe ScriptBlocks przejdź do RemainingScripts
    2. Zamów wszystkie skrypty jako: Begin> Process> Remaining> End
    3. Rezultatem zamówienia jest kolekcja ScriptBlocks. Nazwijmy tę kolekcję OrderedScriptBlocks

      • W przypadku rozpoczęcia/zakończenia nie są związani, zignoruj ​​
    4. (wewnętrznie) parametry ponownego wiązania oparte na OrderedScriptBlocks

      • OrderedScriptBlocks [ 0] zaczyna się
      • OrderedScriptBlocks [1 ..-2] Proces się
      • OrderedScriptBlocks [1] (ten ostatni) staje Koniec

Weźmy tego przykładu

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

aby powstało to:

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

Teraz wynik wykonania jest całkowicie przewidywalna:

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 

To tajemnica -RemainingScripts i teraz rozumiemy bardziej wewnętrzny zachowanie ForEach-Object!

Wciąż muszę przyznać, że nie ma dokumentacji do wspierania mylę (nie moja wina!), Ale te przypadki testowe powinny być wystarczające, aby wyjaśnić zachowanie opisałem.

1

Oto jego szczegóły. ValueFromRemainingArguments jest ustawione na true, więc twoje przypuszczenie jest poprawne.

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

Dzięki Andy, odpowiedziałeś punkt # 1: „parametr -RemainingScripts podejmie wszelkie niezwiązane ScriptBlocks”, ale nie wyjaśnia punkt # 2 „Po 3 bloki są przekazywane w, jednak pierwszy z nich idzie do -Process ... ", który jest w rzeczywistości głównym powodem tego pytania. Zrobiłem więcej badań i teraz myślę, że mogę odpowiedzieć na #point 2. Mimo to doceniam twoją odpowiedź i technikę sprawdzania parametrów. I przepraszam, nie mam pozwolenia na głosowanie na twoją odpowiedź, mimo że jest to bardzo przydatne. –

Powiązane problemy