Tech Blog‎ > ‎

PowerShell - Control Code Execution

posted Feb 3, 2010, 1:51 PM by Victor Zakharov   [ updated Feb 7, 2010, 10:14 AM ]
In this article we are going to discuss basics of how to control code execution in a PowerShell script. There are several keywords, which have slightly different behavior: BREAK, RETURN and EXIT. To help you better understand what is going on, I will post all code extracts in green and outputs in blue.

Break

Just as in C++, you can use break to exit loops early:

#Sample 1. Break a FOR loop
for($i=0; $i -lt 5; $i++)
{
    Write-Host "Start iteration $i"
    if($i -eq 2) { Write-Host "Break"; break }
    Write-Host "Finish iteration $i"
}
Write-Host "End of script reached!" #will be executed
Start iteration 0
Finish iteration 0
Start iteration 1
Finish iteration 1
Start iteration 2
Break
End of script reached!

In this scenario, whatever is written after the FOR loop will be executed. The most important thing to understand is that it gets you up exactly one structural level, no matter where in the code you are. Have a look at the following samples:

#Sample 2. Break a script
Write-Host "Line1"
break
Write-Host "Line2" #this line will not executed
Start

#Sample 3. Break a pipeline (incorrect BREAK usage)
#Task: print numbers 1 through 5, stop when current number is greater than 2
1..5 | % {  
    Write-Host "Start iteration $_"
    if($_ -gt 2) { Write-Host "Break"; break }
    Write-Host "Finish iteration $_"
}
Write-Host "End of script reached!" #not gonna work, read explanation below

Start iteration 1
Finish iteration 1
Start iteration 2
Finish iteration 2
Start iteration 3
Break

We have come up to an interesting point. For some reason, BREAK statement put inside a FOREACH pipeline 'thinks' it's being executed outside the pipeline, so in Sample 3 it will exit the script. In other words, if you put a line after 1..5 loop, it will not be executed. Ask Microsoft about that. :)

There is a workaround, however. Just rewrite your code to use a simple FOREACH loop and not a pipeline. For Sample 3, the new code will look similar to this:

#Sample 4. Break a FOREACH loop (correct BREAK usage)
#Task: print numbers 1 through 5, stop when current number is greater than 2
foreach ( $number in 1..5 ) {  
    Write-Host "Start iteration $number"
    if($number -gt 2) { break }
    Write-Host "Finish iteration $number"
}
Write-Host "End of script reached!" #will run perfectly

Start iteration 1
Finish iteration 1
Start iteration 2
Finish iteration 2
Start iteration 3
End of script reached!

Return

Let's start with Sample 1 and put RETURN statement instead of BREAK:

#Sample 5. Return from a FOR loop
for($i=0; $i -lt 5; $i++)
{
    Write-Host "Start iteration $i"
    if($i -eq 2) { Write-Host "Return"; return }
    Write-Host "Finish iteration $i"
}
Write-Host "End of script reached!" #will NOT be executed

Start iteration 0
Finish iteration 0
Start iteration 1
Finish iteration 1
Start iteration 2
Return

In this case we get same results, however everything after the loop is not executed, because the program goes up one functional level, which includes functions, pipelines and, of course, scripts. Ordinary loops do not count. Take a look at previous samples, changed to RETURN:

#Sample 6. Return from a script
Write-Host "Line1"
return
Write-Host "Line2" #this line will not executed, same as before
Start

Because functional and structural levels are one thing here (=script), behavior is the same for RETURN or BREAK. The following example illustrates how RETURN works for pipelines. Remember, each pipeline iteration is a functional level in terms of PowerShell, so by doing RETURN you only exit current iteration.

#Sample 7. Return from a pipeline
#Task: print numbers 1 through 5, skip when current number is greater than 2
1..5 | % {
    Write-Host "Start iteration $_"
    if($_ -gt 2) { Write-Host "Return"; return }
    Write-Host "Finish iteration $_"
}
Write-Host "End of script reached!" #will be executed
Start iteration 1
Finish iteration 1
Start iteration 2
Finish iteration 2
Start iteration 3
Return
Start iteration 4
Return
Start iteration 5
Return
End of script reached!

#Sample 8. Return from a FOREACH loop
#Task: print numbers 1 through 5, stop when current number is greater than 2
foreach ( $number in 1..5 ) { 
    Write-Host "Start iteration $number"
    if($number -gt 2) { return }
    Write-Host "Finish iteration $number"
}
Write-Host "End of script reached!" #will not be executed
Start iteration 1
Finish iteration 1
Start iteration 2
Finish iteration 2
Start iteration 3

Exit

This appears to be most simple. EXIT works on the script level, regardless of where it's put. While being less flexible, it is often used as emergency action to stop script execution when exception takes place. You might wanna use it when you need to quit your complicated script structure, if results are available early.
---
I will update this article as I progress on this topic and will probably post more code samples.
Comments