From this StackOverflow answer:

P.S. Unfortunately, it is not that easy to simulate break in ForEach-Object.

Argh. That's putting it mildly.

Here's some slightly amended code from the StackOverflow question to which the above quote is responding:

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

ForEach (1..100 in $range){
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

Guess which one works and which doesn't?

The crux seems to be that the ForEach loop, where ForEach leads the line, is a conventional loop, but the Foreach-Object that you so commonly see with the % shorthand, but also when ForEach is used ANYWHERE other than the start of a line is a "looping cmdlet" that's going to freight-train from the start to the finish of your range.

How complicated is this? Check this blog post (that I stole the phrase "looping cmdlet" from)...

First it is important to understand that foreach -ne foreach-object unless used in the pipeline.

(emphasis is mine)

ForEach should be ForEach, if you ask me, but luckily nobody did, as I think the point is that when you're mid-pipeline, there's no real way to refer to what came down from the pipeline into your command, afaik.

That is, this is meaningless...

1..100 | ForEach ($myInt in $PIPELINE_VALUE) { if ($myInt % 2) { $myInt } }

... because $PIPELINE_VALUE doesn't exist. Pipeline values are only implicitly sent along -- unless, of course, you're pulling them up as a parameter in a module or function. Creating a $PIPELINE_VALUE variable, though horribly useful for .NET-threaded heads like mine, would break [2] PowerShell's useful pipeline paradigm.

The limitation with the lack of break, of course, is that you can't do this...

1..100 | % { if ($_ % 2) { $_ }; if ($_ % 30) STOP_EVERYTHING!!!1! }

You're going to run through all 1 to 100, afaict. breaking here would drop the whole pipeline clanging on the floor. And that's my beef, I guess. Why not change the usage of break in this specific context? It seems to be the exception that would prove the [usefulness] of the [general] rule.

So to ForEach proper, you need a two-stepped process (or you have to use a Where-Object cleverly as described in the SO answers, above).

Fun. I should rename this blog "PowerShell Commando".

[2] Okay, literally that's wrong. It would "encourage breaking from PowerShell's pipeline paradigm for a more .NET-ish one".

Labels: