You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
break in the pipeline: Why does End run in some cases and not in others? #5811
do {
1..10|% { if ($_-gt2) { break }; $_ } |Sort-Object-Descending
} while ($false)
do {
% { foreach ( $iin100..110 ) { $i } } |% { if ($_-gt102) { break }; $_ } |Sort-Object-Descending
} while ($false)
Expected behavior
Either no output or
2
1
0
102
101
100
Actual behavior
102
101
100
Environment data
>$PSVersionTable
Name Value
---------
PSVersion 6.0.0-rc.2
PSEdition Core
GitCommitId v6.0.0-rc.2
OS Microsoft Windows 6.3.9600
Platform Win32NT
PSCompatibleVersions {1.0,2.0,3.0,4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
I don't have an explanation, but the issue is related to using % (ForEach-Object) at the start of the pipeline.
If you replace the initial % {... } with $(...) - which is the more idiomatic way to start a pipeline with an expression - the problem goes away (yields no output, as expected, because Sort-Object's end block doesn't get to run):
do {
$(foreach ( $iin100..110 ) { $i }) |# Use $(...) rather than % {...}% { if ($_-gt102) { break }; $_ } |Sort-Object-Descending
} while ($false)
Still, it would be good to have an explanation for your symptom.
break and continue work dynamically - meaning the break/continue statement searches for an appropriate loop to break from at runtime.
...
Under the covers, the break turns into an exception (always V2 or earlier, V3 onwards if not lexically within a loop statement), the exception is always silent (because you don't really want to think of break as an exception, so it's silent even if we don't find a matching loop.
By that reasoning, I think
do {
1..10|% { if ($_-gt2) { break }; $_ } |Sort-Object-Descending
} while ($false)
outputs nothing because the "exception" thrown by break is not caught until it reaches the do{} which is the first loop encountered. That loop is outside the pipeline, so flow of control continues outside the pipeline and end{} doesn't run. On the other hand I think in
do {
% { foreach ( $iin100..110 ) { $i } } |% { if ($_-gt102) { break }; $_ } |Sort-Object-Descending
} while ($false)
the first loop encountered by the "exception" thrown by break is foreach{}, so flow of control continues at the closing } of the foreach and the pipeline continues as usual from there.
Note that you could have used & instead of % in the first stage of the pipeline and gotten the same result - the key is to ensure the foreach loop statement has not completed before hitting the break statement. As Michael Klement (@mklement0) points out, $() would fully execute the loop before writing anything to the pipeline.
I hadn't even considered the execute-in-full-first aspect - good to know that & { ... } - as opposed to $(...) - can send an expression-based statement's output to the pipeline as it is being produced.
Activity
alx9r commentedon Jan 7, 2018
This arose while trying to reproduce the examples in #3821.
mklement0 commentedon Jan 8, 2018
I don't have an explanation, but the issue is related to using
%(ForEach-Object) at the start of the pipeline.If you replace the initial
% {... }with$(...)- which is the more idiomatic way to start a pipeline with an expression - the problem goes away (yields no output, as expected, becauseSort-Object's end block doesn't get to run):Still, it would be good to have an explanation for your symptom.
alx9r commentedon Jan 8, 2018
@izybkr wrote this in #3897 (comment):
By that reasoning, I think
outputs nothing because the "exception" thrown by
breakis not caught until it reaches thedo{}which is the first loop encountered. That loop is outside the pipeline, so flow of control continues outside the pipeline andend{}doesn't run. On the other hand I think inthe first loop encountered by the "exception" thrown by
breakisforeach{}, so flow of control continues at the closing}of theforeachand the pipeline continues as usual from there.Jason Shirk (@lzybkr) Is this right?
lzybkr commentedon Jan 8, 2018
alx9r - your understanding is correct.
Note that you could have used
&instead of%in the first stage of the pipeline and gotten the same result - the key is to ensure theforeachloop statement has not completed before hitting thebreakstatement. As Michael Klement (@mklement0) points out,$()would fully execute the loop before writing anything to the pipeline.alx9r commentedon Jan 8, 2018
This all makes sense to me now and seems to match the behavior I'm seeing. Thank you Jason Shirk (@lzybkr).
mklement0 commentedon Jan 8, 2018
I hadn't even considered the execute-in-full-first aspect - good to know that
& { ... }- as opposed to$(...)- can send an expression-based statement's output to the pipeline as it is being produced.