Windows PowerShell provides a command-line interface as well as a powerful scripting language. As with any scripting language, PowerShell provides a number of language constructs that let you control the flow of your script as well as make decisions about what it should do. These language constructs are the if, switch, for, break, continue, foreach, while, and do statements.

The if Statement

The if statement uses the syntax:

if (conditional_expression) {
  statement_list
}

The parentheses around the conditional expression are required, as are the curly braces around the statement list. In PowerShell, one or more statements in between curly braces is called a script block. If the conditional expression evaluates as true, PowerShell executes the script block.

If you have list of expressions to test, you can follow the if statement with one or more elseif statements, as follows:

if (conditional_expression_1) {
  statement_list_1
}
elseif (conditional_expression_2) {
  statement_list_2
}
elseif (conditional_expression_3) {
  statement_list_3
}

PowerShell executes an elseif statement's script block only when all the previous conditional expressions evaluate to false. In this example, PowerShell will execute statement_list_3 only if both conditional_expression_1 and conditional_expression_2 are false.

You can also use the else statement after an if or elseif statement. The else statement doesn't use a conditional expression:

if (conditional_expression) {
  statement_list_1
}
else {
  statement_list_2
}

PowerShell executes the statements in the else script block only when all previous conditional expressions evaluate to false.

The switch Statement

If you have a long list of conditional statements to test, the switch statement might let you write more concise and easier-to-read code. In effect, the switch statement is a series of if statements. The switch statement's syntax is:

switch (test_statement) {
  match_expression_1 {
    statement_list_1
  }
  match_expression_2 {
    statement_list_2
  }
  default {
    statement_list_3
  }
}

The switch statement executes test_statement and evaluates its output against each of the match expressions. If any of them match, PowerShell executes the corresponding statements. For example, if the expression results in a match for both match_expression_2 and match_expression_3, PowerShell will execute both statement_list_2 and statement_list_3. In some cases, this might present a problem. For instance, the code in Listing 1 outputs "Day is Friday" twice because there are two matches.

Listing 1: A 'switch' Statement with Duplicate Matches
$dow = "Friday"
switch ($dow) {
  "Friday" {
    "Day is Friday"
  }
  "Tuesday" {
    "Day is Tuesday"
  }
  "Friday" {
    "Day is Friday"
  }
}

To avoid this, you can insert break statements into the statement blocks, as shown in Listing 2. That way, the switch statement will terminate after a match is found.

Listing 2: A 'switch' Statement That Uses the 'break' Statement to Avoid Duplicate Matches
$dow = "Friday"
switch ($dow) {
  "Friday" {
    "Day is Friday"
    break
  }
  "Tuesday" {
    "Day is Tuesday"
    break
  }
  "Friday" {
    "Day is Friday"
    break
  }
}

If none of the match expressions result in a match, PowerShell executes the statements in the script block following the default statement if it's present. The default statement is optional.

The switch statement supports several parameters that modify how the match expressions are treated: -Regex, -Wildcard, -Exact, and -CaseSensitive. The switch statement also provides a -File parameter to read input from a file. For more information about these parameters, see the about_switch PowerShell Help topic.

The for Statement

The for statement, which is also called a for loop, creates a loop that executes statements repeatedly until some condition is met. Its syntax is:

for (initialize; condition; repeat) {
  statement_list
}

The initialize statement lets you set the initial value of a variable, the condition statement tests the variable's value, and the repeat statement executes for each iteration of the loop. For example, the following code will output the three elements in the $items array:

$items = @("A","B","C")
for ($i = 0; $i -lt $items.Count; $i++) {
  $items[$i]
}

The about_for PowerShell Help topic provides an excellent overview of the for loop.

The break and continue Statements

Figure 1 and Figure 2 illustrate the break and continue statements, respectively. In Figure 1, the break statement terminates the for loop after the $i variable reaches 3.

Figure 1: Using the "break" statement

In Figure 2, the continue statement repeats the for loop if the $i variable is less than 3.

Figure 2: Using the "continue" statement

Both the break and continue statements work inside the for, foreach, while, and do loops. (You can also use break with the switch statement, as I discussed previously.) It's important to note that the break and continue statements don't work as expected inside a ForEach-Object loop, which I'll discuss shortly.

The foreach Statement

The foreach statement, or foreach loop, repeats code for each item in a collection. The syntax is:

foreach (item in collection) {
  statement_list
}

When the foreach statement runs, PowerShell repeats statement_list for each item in the collection, replacing the variable you specify for item for each item. For example, the following code outputs the three elements in the $items array:

$items = @("A","B","C")
foreach ($element in $items) {
  $element
}

The foreach statement isn't the same as the ForEach-Object cmdlet. Unfortunately, the ForEach-Object cmdlet has an alias of foreach, which can be very confusing. The basic difference is that if you specify foreach on the right side of the pipe (|) character, PowerShell executes the ForEach-Object cmdlet; otherwise, PowerShell executes the foreach statement.

One reason this difference is important is because the foreach statement supports the break and continue statements, but the ForEach-Object cmdlet doesn't support them. This can cause confusion if the ForEach-Object block occurs within another loop. The best way to illustrate this confusion is by looking at Figure 3 and Figure 4. Figure 3 shows a foreach loop inside a for loop.

Figure 3: Using the "break" Statement in a "foreach" Loop Returns the Expected Results

The break statement terminates the foreach loop, and the for loop executes twice (as expected). Figure 4 shows similar code that uses the ForEach-Object cmdlet instead of a foreach loop. In Figure 4, the break statement terminates the for loop, not the foreach loop, so the code returns unexpected results.

Figure 4: Using the "break" Statement with the ForEach-Object Cmdlet Returns Unexpected Results

Adding to the confusion, you can use the return statement to emulate the behavior of the continue statement, but only when using ForEach-Object!

Another difference between the foreach statement and the ForEach-Object cmdlet is that the foreach statement can access only a complete collection, but the ForEach-Object cmdlet can access a collection's items one item at a time. Another way to say this is that the ForEach-Object cmdlet takes advantage of PowerShell's pipeline, but the foreach statement doesn't.

For example, Listing 3 shows some code that uses the foreach loop to retrieve all the files in the Windows directory. When you run this code, it might take some time before you see any output because PowerShell retrieves all the items before the foreach loop starts running.

Listing 3: Code That Uses the 'foreach' Statement
# Retrieves the entire collection before accessing each item
$path = $ENV:SystemRoot
$files = Get-ChildItem -Path $path -Recurse
foreach ($file in $files) {
  $file.FullName
}

Listing 4 shows how to do the same thing using the ForEach-Object cmdlet and the pipeline. The code in Listing 4 will start displaying the results sooner than the code in Listing 3 because PowerShell outputs the items as it accesses them.

Listing 4: Code That Uses the ForEach-Object Cmdlet
# Accesses each item as it passes through the pipeline
$path = $ENV:SystemRoot
Get-ChildItem -Path $path -Recurse | ForEach-Object {
  $_.FullName
}

Table 1 lists the main differences between the foreach statement and the ForEach-Object cmdlet. Which one you choose depends on the needs of your script. For example, if you want to display a progress percentage bar using the Write-Progress cmdlet, you need to gather the entire collection first so you can count the total number of items. However, if gathering the entire collection is very time-consuming, using the ForEach-Object cmdlet might be preferable.

  foreach Statement ForEach-Object Cmdlet
Table 1: Differences Between the "foreach" Statement and the ForEach-Object Cmdlet
Statement type Built-in language statement Cmdlet
Supports the break and continue statements Yes No*
Supports the pipeline No Yes
Requires the entire collection before looping Yes No
*You can use the return statement in a ForEach-Object script block to emulate continue.

The while Statement

The while statement, or while loop, repeats code as long as a conditional expression evaluates to true. Its syntax is:

while (conditional_expression) {
  statement_list
}

The while statement might not execute its script block if the conditional expression initially evaluates as false. For example, consider this code:

$number = 10
while ($number -le 5) {
  Write-Host "Number is $number"
  $number++
}

The statements inside this while loop will never execute because the $number variable is greater than 5. However, if you set $number to 3, the loop will execute three times (for the values of 3, 4, and 5).

The while statement is useful when you want to create a loop that executes infinitely until some condition inside the loop is met. Listing 5 shows a short script that repeats until you enter a random number.

Listing 5: Code That Uses an Infinite 'while' Loop
$magicNumber = Get-Random -Minimum 1 -Maximum 10
while ($true) {
  $guess = Read-Host "Enter the magic number"
  if ($guess -eq $magicNumber) {
    break
  }
  Write-Host "Sorry, try again"
}

The do Statement

The do statement, or do loop, is similar to the while loop, except that PowerShell evaluates the conditional expression at the end of the loop rather than at the beginning. Because PowerShell executes the statements in the do loop before evaluating the conditional expression, PowerShell will always execute the statements in a do loop at least once.

The do statement can be used with either the while or until keyword. When you use the while keyword, the syntax is:

do {
  statement_list
} while (conditional_expression)

When you use the until keyword, the syntax is:

do {
  statement_list
} until (conditional_expression)

If you use the while keyword, the loop repeats if the conditional expression is true. If you use the until keyword, the loop repeats if the conditional expression is false.

It's up to you whether you want to use while or until in your do loop. Listing 6 and Listing 7 show the same loop using slightly different code. There are only two differences between the two loops: The loop in Listing 6 uses the -eq operator and the while keyword, whereas the loop in Listing 7 uses the -ne operator and the until keyword.

Listing 6: Code that Uses the 'do' Statement with the 'while' Keyword
do {
  $name = Read-Host "Enter your name"
}
while ($name -eq "")
Write-Host "Hello, $name"

In my opinion, the loop in Listing 7 is slightly more intuitive, but you can use whichever one improves the readability of your script.

Listing 7: Code that Uses the 'do' Statement with the 'until' Keyword
do {
  $name = Read-Host "Enter your name"
}
until ($name -ne "")
Write-Host "Hello, $name"

Go with the Flow

The if, switch, for, break, continue, foreach, while, and do statements are the core PowerShell language statements that let you control the flow of your script as well as make decisions about what it should do. If you master these core statements, your PowerShell scripts will become more efficient and effective.