Why does ForEach-Object require its script block on the same line?

0
0
Asked By MellowCedar47 On

I usually put an opening brace on the same line when a block contains one statement, but put it on the next line for multi-line blocks. That works fine with language constructs such as `if` and `foreach`, yet it causes problems with `ForEach-Object`. For example, placing the script block on the following line can produce a parameter prompt or an error. Why does PowerShell treat these cases differently, and what formatting style is recommended?

4 Answers

Answered By NorthVale63 On

PowerShell generally assumes a newline ends a statement unless there is an explicit continuation or the syntax clearly requires more input. Unlike languages with a semicolon terminator, it cannot always tell whether the next line is meant to continue a command or start a new one. Keeping the opening brace on the same line makes it unambiguous that the script block belongs to the cmdlet:

`'foo' | ForEach-Object {`
` $_`
`}`

You can also specify the parameter explicitly with `-Process`, but the brace still needs to begin on that command line.

Answered By IvoryMaple92 On

The `foreach` keyword and `ForEach-Object` cmdlet may look similar, but they serve different purposes. The keyword iterates over a collection in expression mode, while the cmdlet processes objects arriving through the pipeline and receives its action as a parameter. That difference in parsing explains why this works:

`foreach ($item in $items)`
`{`
` $item.Name`
`}`

but the equivalent pipeline form should keep the block attached to the cmdlet:

`$items | ForEach-Object {`
` $_.Name`
`}`

Answered By SableOrbit5 On

The practical answer is to choose one brace style and apply it consistently. A common PowerShell convention is the opening brace on the same line for both keywords and cmdlets:

`if ($condition) {`
` Do-Something`
`}`

`$items | ForEach-Object {`
` Do-Something $_`
`}`

Putting the brace on a new line is not inherently invalid for every construct, but it exposes this parsing limitation and can make code look inconsistent to people maintaining it later.

CopperLynx31 -

For larger or reusable logic, assigning the script block to a variable or using an advanced function can make the code easier to organize. But pipeline processing with `ForEach-Object` is not merely a quick-and-dirty substitute for the `foreach` keyword; streaming and collection-based iteration have different trade-offs.

Answered By QuartzHarbor8 On

`ForEach-Object` is a cmdlet, not a language keyword. The script block inside `{}` is actually an argument to the cmdlet, usually passed to `-Process`. PowerShell parses commands in argument mode, where the end of the line normally ends the command. So this can be interpreted as a command with no script-block argument followed by a separate script block:

`'foo' | ForEach-Object`
`{ $_ }`

By contrast, `if` and `foreach` are language constructs parsed in expression mode. The parser knows that a block must follow them, so whitespace and line breaks are more flexible. The same issue applies to other cmdlets that accept script blocks, such as `Where-Object` and `Invoke-Command`; it is not unique to `ForEach-Object`.

BrightPine_26 -

This is also why a line break after a parameter can be ambiguous. For example, `Get-Process -Id` followed by `$PID` is treated as a parameter without its required argument, rather than as one continued statement.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.