The difference is that when you pipe the array into Chunk-Object, the function executes the process block once for each element in the array passed as a sequence of pipeline objects, whereas when you pass the array as an argument to the -InputObject parameter, the process block executes once for the entire array, which is assigned as a whole to $InputObject.
So, let's take a look at your pipeline version of the command:
echo 1 2 3 4 5 6 7 | Chunk-Object -Process {$_ + 100} -ElementsPerChunk 3
The reason this one works is that for each iteration of the pipeline, $_ is set to the value of the current array element in the pipeline, which is also assigned to the $InputObject variable (as a single-element array, due to the [object[]]
typecast. The foreach loop is actually extraneous in this case, because the $InputObject array always has a single element for each invocation of the process block. You could actually remove the loop and change $current_element = $o
to $current_element = $InputObject
, and you'd get the exact same results.
Now, let's examine the version that passes an array argument to -InputObject:
Chunk-Object -InputObject (echo 1 2 3 4 5 6 7) -Process {$_ + 100} -ElementsPerChunk 3
The reason this doesn't work is that the scriptblock you're passing to the -Process parameter contains $_, but the foreach loop assigns each element to $o, and $_ isn't defined anywhere. All elements in the results are 100 because each iteration sets $current_element to the results of the scriptblock {$_ + 100}
, which always evaluates to 100 when $_ is null. To prove this out, try changing $_ in the scriptblock to $o, and you'll get the expected results:
Chunk-Object -InputObject (echo 1 2 3 4 5 6 7) -Process {$o + 100} -ElementsPerChunk 3
If you want to be able to use $_ in the scriptblock, change the foreach loop to a pipeline, by simply replacing foreach($o in $InputObject) {
with $InputObject | %{
. That way both versions will work, because the Chunk-Object function uses a pipeline internally, so $_ is set sequentially to each element of the array, regardless of whether the process block is invoked multiple times for a series of individual array elements passed in as pipeline input, or just once for a multiple-element array.
UPDATE:
I looked at this again and noticed that in the line
$current_element = & $Process $current_element;
you appear to be trying to pass $current_element as an argument to the scriptblock in $Process. This doesn't work because parameters passed to a scriptblock work largely the same as in functions. If you invoke MyFunction 'foo'
, then 'foo' isn't automatically assigned to $_ within the function; likewise, & {$_ + 100} 'foo'
doesn't set $_ to 'foo'. Change your scriptblock argument to {$args[0] + 100}
, and you'll get the expected results with or without passing in pipeline input:
Chunk-Object -InputObject (echo 1 2 3 4 5 6 7) -Process {$args[0] + 100} -ElementsPerChunk 3
Note that although this version of the scriptblock argument works even if you keep the foreach loop, I'd still recommend using Foreach-Object ($InputObject | %{
), because it's generally more efficient, so the function will run faster for large amounts of data.