do {
1..10 |
% { if ($_ -gt 2) { break }; $_ } |
Sort-Object -Descending
} while ($false)
do {
% { foreach ( $i in 100..110 ) { $i } } |
% { if ($_ -gt 102) { break }; $_ } |
Sort-Object -Descending
} while ($false)
Either no output or
2
1
0
102
101
100
102
101
100
> $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
This arose while trying to reproduce the examples in #3821.
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 ( $i in 100..110 ) { $i }) | # Use $(...) rather than % {...}
% { if ($_ -gt 102) { break }; $_ } |
Sort-Object -Descending
} while ($false)
Still, it would be good to have an explanation for your symptom.
@izybkr wrote this in #3897 (comment):
breakandcontinuework dynamically - meaning thebreak/continuestatement searches for an appropriate loop to break from at runtime.
...
Under the covers, thebreakturns 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 ofbreakas an exception, so it's silent even if we don't find a matching loop.
By that reasoning, I _think_
do {
1..10 |
% { if ($_ -gt 2) { 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 ( $i in 100..110 ) { $i } } |
% { if ($_ -gt 102) { 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.
@lzybkr Is this right?
@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 the foreach loop statement has not completed before hitting the break statement. As @mklement0 points out, $() would fully execute the loop before writing anything to the pipeline.
This all makes sense to me now and seems to match the behavior I'm seeing. Thank you @lzybkr.
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.
Most helpful comment
@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 @mklement0 points out,$()would fully execute the loop before writing anything to the pipeline.