I cannot use begin/process/end block in a ForEach-Object?

3.9k views Asked by At

I've got the following code:

function f()
{
    begin{$count=0}
    process{$count+=10}
    end{$count}
}
1..10|f # OK
1..10|%{
    begin{$count=0}
    process{$count+=10}
    end{$count}
} # Error

The first "f" call succeeds, while the %{} block shows error:

100
% : The script block cannot be invoked because it contains more than one clause. The Invoke() method can only be used on script blocks that contain a single clause.
At D:\Untitled1.ps1:13 char:7
+ 1..10|%{
+       ~~
    + CategoryInfo          : InvalidOperation: (:) [ForEach-Object], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.ForEachObjectCommand

But why? ForEach-Object doesn't support begin/process/end blocks?

3

There are 3 answers

0
Mathias R. Jessen On

ForEach-Object takes the individual blocks as separate named parameters.

In your case, that would be:

1..10 |ForEach-Object -Begin {$count=0} -Process {$count+=10} -End {$count}

This is well documented in the help files - from Get-Help ForEach-Object -Parameter *:

-Begin <ScriptBlock>
    Specifies a script block that runs before processing any input objects.

    Required?                    false
    Position?                    named
    Default value                None
    Accept pipeline input?       false
    Accept wildcard characters?  false


-End <ScriptBlock>
    Specifies a script block that runs after processing all input objects.

    Required?                    false
    Position?                    named
    Default value                None
    Accept pipeline input?       false
    Accept wildcard characters?  false

<# ... #>

-Process <ScriptBlock[]>
    Specifies the operation that is performed on each input object. Enter a script
    block that describes the operation.

    Required?                    true
    Position?                    1
    Default value                None
    Accept pipeline input?       false
    Accept wildcard characters?  false
0
js2010 On

A cool thing is, you can do it without specifying the parameters, and it works, using -process and -remainingscripts behind the scenes.

1..10 | ForEach {$count=0} {$count+=10} {$count}

100
0
ThePennyDrops On

As an addendum, just a small point if you like to structure your code for ease of reading. The continuation tick ("`") is important - as I just found out to some cost to myself!

Get-Content -Path $SelectedFiles | ForEach-Object `
-Begin {
 #- Do Begin stuff -----
 } `
-Process {
#- Do Process stuff
} `
-End {
#- Do End stuff
}
#- EndOf: ForEach is here -----
#- NB! Continuation tick "`" must be preceded by space & be LAST character on line!!!!!

To help in the ISE, the colour of the parameters (Begin, Process, End) should be the same as other parameters (my parameters are black) when correctly identified. Play with the above code in the ISE, put a space after a tick, don't have a space before a tick, etc. I didn't realise how important a 'space' could be - grin! Good luck! Hope this helps another beginner like me.