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
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.
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`
`}`
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.
`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`.
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.

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.