PowerShell: the mysterious -RemainingScripts parameter of ForEach-Object -


short question: has detailed information on -remainingscripts parameter of foreach-object?

long question:

i started learning powershell last week , i'm going through each cmdlets learn more details. based on public documentation, know foreach-object can have begin-process-end blocks, this:

get-childitem | foreach -begin { "block1";     $filecount = $directorycount = 0} -process { "block2";     if ($_.psiscontainer) {$directorycount++} else {$filecount++}} -end {     "block3"; "$directorycount directories , $filecount files"} 

result expected: 1 time "block1" , "block3", "block2" repeated each item passed in, , dir count/file count correct. far good.

now, what's interesting is, following command works , gives same result:

get-childitem | foreach { "block1"     $filecount = $directorycount = 0}{ "block2";     if ($_.psiscontainer) {$directorycount++} else {$filecount++}}{     "block3"; "$directorycount directories , $filecount files"} 

just 3 scriptblocks passed foreach. based on manual, first 1 goes -process (position 1). how remaining 2? according manual, there's not parameter "position 2". turned trace-command, , found later 2 script blocks remainingscripts "ilist 2 elements".

bind arg [$filecount = $directorycount = 0] parameter [process] bind arg [system.management.automation.scriptblock[]] param [process] successful bind arg [system.collections.arraylist] parameter [remainingscripts] bind arg [system.management.automation.scriptblock[]] param [remainingscripts] successful 

so if change command this:

# no difference with/without comma "," between last 2 blocks get-childitem | foreach -process { "block1"     $filecount = $directorycount = 0} -remainingscripts { "block2";     if ($_.psiscontainer) {$directorycount++} else {$filecount++}},{     "block3"; "$directorycount directories , $filecount files"} 

still, same result.

so noticed, 3 commands give same result. raises interesting question: both of later 2 commands (implicitly) specified -process, foreach-object surprisingly ends using argument of -process "-begin"! (script block executed once @ beginning).

this experimentation suggests:

  1. -remainingscripts parameter take unbound scriptblocks
  2. when 3 blocks passed in, although first 1 goes -process, later used "begin" while remaining 2 become "process" , "end"

still, above wild guess. didn't find documentation support guess

so, go short question :) has detailed information on -remainingscripts parameter of foreach-object?

thanks.

i did more research , feel confident answer behavior of -remainingscripts parameter when multiple scriptblocks passed in.

if run following commands , inspect result carefully, find pattern. it's not quite straightforward, still not hard figure out.

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

so what's pattern here?

  • when there's single scriptblock passed in: easy, goes -process (the common usage)

  • when 2 scriptblocks passed in, there 3 possible combinations

    1. -process & -begin -> execute specified
    2. -process & -end -> execute specified
    3. -process & -remainingscripts -> process becomes begin, while remainingscripts becomes process

if run these 2 statements:

1..5 | foreach { "process block" } { "remain block" } 1..5 | foreach { "remain block" }  -process { "process block" }  # both of them return: process block remain block remain block remain block remain block remain block 

as find out, special case of following test case:

  • when more 2 scriptblocks passed in, follow workflow:

    1. bind scriptblocks specified (begin,process,end); remaining scriptblocks go remainingscripts
    2. order scripts as: begin > process > remaining > end
    3. result of ordering collection of scriptblocks. let's call collection orderedscriptblocks

      • if begin/end not bound, ignore
    4. (internally) re-bind parameters based on orderedscriptblocks

      • orderedscriptblocks[0] becomes begin
      • orderedscriptblocks[1..-2] become process
      • orderedscriptblocks[-1] (the last one) becomes end

let's take example

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

order result is:

{ "process block" }    # new begin { "remain block 1" }   # new process { "remain block 2" }   # new process { "remain block 3" }   # new end 

now execution result predictable:

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 

that's secret behind -remainingscripts , understand more internal behavior of foreach-object!

still have admit there's no documentation support guess (not fault!), these test cases should enough explain behavior described.


Comments

Popular posts from this blog

.htaccess - First slash is removed after domain when entering a webpage in the browser -

Automatically create pages in phpfox -

c# - Farseer ContactListener is not working -