Powershell: The 'break' will finish the function ?

Created on 15 Mar 2018  路  6Comments  路  Source: PowerShell/PowerShell

function say2(){
    $res = $false;
    $arr = 1..10
    $arr | ForEach-Object{
         if($_ -eq 5){
            $res = $true
            break;
         }
    }
    write-host "im here!"
    return $res
}
$result = say2
write-host $result

why does the function 'say2' return null ? after the break, it seem to jump out the 'say2' function!!

Any response will be appreciated. thanks in advance.

Issue-Question Resolution-Answered

Most helpful comment

@mklement0, Thanks for the explanation on the difference of foreach and foreach-object which doesn't behave the same way.

@guguji5, was just looking into foreach-object in your sample provided.
:)

All 6 comments

Simple! Beside the fact that you are not displaying any results before the break then the value is gone as it never reaches the Write-Host line,

There's a couple of ways to tackle this:

  1. Add the Write-Host before the break.
  2. Or, just use the a "return" to exit the Foreach to properly terminate the function.
function say2()
{
    $res = $false;
    $arr = 1 .. 10
    $arr | ForEach-Object{
        if ($_ -eq 5)
        {
            $res = $true
            return 
        }
    }
    write-host "im here!"
    return $res
}
say2

function say2()
{
    $res = $false;
    $arr = 1 .. 10
    $arr | ForEach-Object{
        if ($_ -eq 5)
        {
            $res = $true
            write-host "im here! breaking = $res";
            break
        }
    }
    write-host "im here!"
    return $res
}
say2

Sorry! Answer to your question: Yes! 'break' will terminate the function and all variables are discarded.
:)

@MaximoTrinidad It's extremely grateful to your response.
I know the execution order well, but I don't accept your answer.Look at the code below:

function say1(){
    $arr = 1..10
    $result = 0
    foreach($item in $arr){
        if($item -eq 5){
            $result = 5
            break
        }
    }
    write-host "im here!"
    return $result;
}
$result1 = say1
write-host $result1

image
Obviously after the break it executes the return , so it's not the break that terminates the function.
I guess the pipeline of foreach-object causes that weird result.I will search more materials to figure it out.

It's important to note the behavior of $arr | ForEach-Object{} and foreach($item in $arr){} are different.

When using Foreach-Object {} the, script block you supply is actually the positional parameter for the -Process parameter. When you use this method, the code acts like the Process{} block on an advanced function. The proper way to short-circuit a Process block is the return statement. When you use break in this context it actually affects whatever outer looping construct that is affected by break and not the Foreach-Object {} loop.

You can see it demonstrated here:

'Before Foreach'
foreach ($i in 1..5) {
    'Before Foreach-Object {0}' -f $i
    -2..$i | ForEach-Object {
        'In Foreach-Object {0}' -f $_
        if ($_ -eq 3) {
            'Break from Foreach-Object {0}' -f $_
            break
        }
        'After if in  Foreach-Object {0}' -f $_
    }
    'After Foreach-Object {0}' -f $i
}
'After Foreach {0}' -f $i

The result:

Before Foreach
Before Foreach-Object 1
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
After Foreach-Object 1
Before Foreach-Object 2
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
In Foreach-Object 2
After if in  Foreach-Object 2
After Foreach-Object 2
Before Foreach-Object 3
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
In Foreach-Object 2
After if in  Foreach-Object 2
In Foreach-Object 3
Break from Foreach-Object 3
After Foreach 3

You can see that the After Foreach-Object 3 is never reached. and that the outer foreach ($i in $c){} loop is broken out of.

Compare that with this which uses return instead of break in the Foreach-Object {}:

'Before Foreach'
foreach ($i in 1..5) {
    'Before Foreach-Object {0}' -f $i
    -2..$i | ForEach-Object {
        'In Foreach-Object {0}' -f $_
        if ($_ -eq 3) {
            'Break from Foreach-Object {0}' -f $_
            return
        }
        'After if in  Foreach-Object {0}' -f $_
    }
    'After Foreach-Object {0}' -f $i
}
'After Foreach {0}' -f $i

Result

Before Foreach
Before Foreach-Object 1
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
After Foreach-Object 1
Before Foreach-Object 2
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
In Foreach-Object 2
After if in  Foreach-Object 2
After Foreach-Object 2
Before Foreach-Object 3
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
In Foreach-Object 2
After if in  Foreach-Object 2
In Foreach-Object 3
Break from Foreach-Object 3
After Foreach-Object 3
Before Foreach-Object 4
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
In Foreach-Object 2
After if in  Foreach-Object 2
In Foreach-Object 3
Break from Foreach-Object 3
In Foreach-Object 4
After if in  Foreach-Object 4
After Foreach-Object 4
Before Foreach-Object 5
In Foreach-Object -2
After if in  Foreach-Object -2
In Foreach-Object -1
After if in  Foreach-Object -1
In Foreach-Object 0
After if in  Foreach-Object 0
In Foreach-Object 1
After if in  Foreach-Object 1
In Foreach-Object 2
After if in  Foreach-Object 2
In Foreach-Object 3
Break from Foreach-Object 3
In Foreach-Object 4
After if in  Foreach-Object 4
In Foreach-Object 5
After if in  Foreach-Object 5
After Foreach-Object 5
After Foreach 5

Specifically this:

In Foreach-Object 3
Break from Foreach-Object 3
In Foreach-Object 4

Notice how that pattern then repeats. That is because only the Foreach-Object {} loop is being broken out of and the outer foreach ($i in $c){} loop continues to operate.

The confusing part in this is that Foreach is an alias of Foreach-Object but effectively Foreach ($I in $c){} is a different looping construct from Foreach-Object {}. continue and break should be used in Foreach ($i in $c){} and return should be used in Foreach-Object.

For background information on break and continue, see https://github.com/PowerShell/PowerShell/issues/3879#issuecomment-304940545.

To add to @markekraus's explanation: It's important to note that return in the ForEach-Object cmdlet's -Process block is the equivalent of continue inside a genuine looping construct such as foreach ($v in ...) { ... }.

By contrast, a genuine loop's break keyword has _no_ ForEach-Object equivalent; in other words: exiting the pipeline prematurely, on demand is not directly supported.

See https://github.com/PowerShell/PowerShell/issues/3879#issuecomment-304991572

@mklement0, Thanks for the explanation on the difference of foreach and foreach-object which doesn't behave the same way.

@guguji5, was just looking into foreach-object in your sample provided.
:)

@MaximoTrinidad @markekraus @mklement0 Thanks for your explanation.This issue leads me a deep understanding in powershell.
I change the foreach-object to the foreach and it works as I expect.

Was this page helpful?
0 / 5 - 0 ratings