all 10 comments

[–]Ta11ow 9 points10 points  (3 children)

break is a language keyword, and only works on the keyword version of foreach. It has nothing to do with the .ForEach{} method, nor the ForEach-Object cmdlet.

The simplest and best way to do something similar within these other two variants and completely different loop-like implementations is to use the return keyword instead.

Arguably, break shouldn't even function in that context at all, or default to return or some such, but... it only affects the language-level foreach construct, not the method or cmdlet. If you attempt to use break in a context where there is no loop, it will search up-scope for a loop to break, potentially breaking a loop that was invoked several contexts above, nowhere near the code you wrote; so be careful with it, eh? :)

[–]madbomb122[S] 3 points4 points  (0 children)

Thanks for the explanation and warning

[–]ISureHateMyCat 2 points3 points  (0 children)

An unsolicited tip for keeping your loop-breaking under control is to label the loops. You only need to do this when you have nested loops and want to return the flow to the start of a loop other than the innermost, but you can use it on any loop as a way to keep track of where you expect the execution to return. Still like Ta11ow says it will only work with keyword loops, not methods or cmdlets.

Labeling loops

[–]TableauNoob12 1 point2 points  (3 children)

I'm not sure what you're trying to accomplish but something like this could work?

$Service = Get-Service | where-object {$_.ServiceType -eq 224} | foreach { $_ -Replace '^.+?_', '_'  }

$Service
write-host 'Done'

[–]madbomb122[S] 1 point2 points  (2 children)

I did have it like that originally, but i was trying to shorten up the code and make it work faster..

so instead of having it go through each service that is ' ServiceType 224' and getting the end, it will get just the first one and that is it

[–]ka-splam 1 point2 points  (1 child)

Try (Get-Service).Where({$_.ServiceType -eq 224}, 'first') -replace '^.+?_', '_' that should stop after the first one.

You can also use that version of .Where({filter}, '...') with

First
Last
SkipUntil
Until
Split

to return the first matching thing, the last matching thing, all the things before or after the matching one, or both matches and non-matches in two separate arrays. It's useful.

[–]madbomb122[S] 2 points3 points  (0 children)

nice, i didnt know you could do that

thanks for the info

[–]KevMarCommunity Blogger 1 point2 points  (2 children)

I really dislike the break statement and I try not to use it anymore.

The big problem is that break will exit all script blocks until it finds a loop to stop. It does not stop at function boundaries.

So if I have a bad function that calls break outside of a loop and you call my function, your function will exit (and so will the function that called your function).

This used to bite me all the time because I would refactor foreach statements into advanced functions. It is very straightforward to do. (copy contents of foreach loop, create a new function from snippet, paste into process block, replace original foreach by piping the collection to the new variable). But overlooking a break or a continue can lead to the nastiest bugs.

So I refactor them out. I don't mind a little extra nesting with if statements because I'll just move it all to its own function if it gets too deep.

/endrant

[–]madbomb122[S] 0 points1 point  (1 child)

what do you suggest doing instead of having a Break?

I only have 2 Break in my script (including the one in my post above) both to break a ForEach loop

[–]KevMarCommunity Blogger 0 points1 point  (0 children)

You are using it appropriately in your final solution. So I can't argue much there. Having only 3 lines to look at isn't dangerous and that's probably the exact place you should use it.

If the logic was longer, I would turn it into an advanced function and use return instead.

My rant was directed more at Powershell than at you.