Control Flow¶
Control flow statements direct the order of execution in a script — branching, looping, and switching.
If / ElseIf / Else¶
$age = 20
if ($age -lt 13) {
"Child"
} elseif ($age -lt 18) {
"Teenager"
} elseif ($age -lt 65) {
"Adult"
} else {
"Senior"
}
Negation¶
Inline / ternary-style (PS 7+)¶
Switch Statement¶
switch is a multi-branch selector — more powerful than most languages because it supports wildcards, regex, and custom conditions.
$day = "Monday"
switch ($day) {
"Saturday" { "Weekend!" }
"Sunday" { "Weekend!" }
default { "Weekday" }
}
Multiple patterns in one branch¶
Wildcard matching¶
switch -Wildcard ($filename) {
"*.txt" { "Text file" }
"*.csv" { "CSV file" }
"*.ps1" { "PowerShell script" }
default { "Unknown type" }
}
Regex matching¶
switch -Regex ($input) {
"^\d{4}-\d{2}-\d{2}$" { "ISO date" }
"^\d+$" { "Integer" }
"^\d+\.\d+$" { "Decimal" }
default { "Other" }
}
Iterating an array with switch¶
$items = "apple","BANANA","Cherry"
switch -CaseSensitive ($items) {
{ $_ -cmatch "^[A-Z]" } { "Starts with uppercase: $_" }
}
For Loop¶
for ($i = 0; $i -lt 5; $i++) {
"Iteration $i"
}
# Countdown
for ($i = 10; $i -ge 0; $i--) {
"T-$i..."
}
ForEach Loop¶
$fruits = @("apple", "banana", "cherry")
foreach ($fruit in $fruits) {
Write-Host "Fruit: $fruit"
}
# Iterate over files
foreach ($file in Get-ChildItem .\logs -Filter *.log) {
"Processing: $($file.Name)"
}
ForEach-Object (pipeline version)¶
Get-ChildItem | ForEach-Object { "File: $($_.Name)" }
# Shorthand
Get-ChildItem | % { "File: $($_.Name)" }
While Loop¶
Wait for a condition¶
$service = "wuauserv"
while ((Get-Service $service).Status -ne 'Running') {
Write-Host "Waiting for $service..."
Start-Sleep -Seconds 2
}
Write-Host "$service is running"
Do-While and Do-Until¶
# Do-While: runs at least once, continues while condition is true
$i = 0
do {
"i = $i"
$i++
} while ($i -lt 3)
# Do-Until: runs at least once, continues until condition is true
do {
$response = Read-Host "Enter 'yes' to continue"
} until ($response -eq "yes")
Loop Control: break, continue, return¶
# break — exit the loop immediately
foreach ($n in 1..100) {
if ($n -gt 5) { break }
$n
}
# continue — skip to next iteration
foreach ($n in 1..10) {
if ($n % 2 -eq 0) { continue }
"$n is odd"
}
# return — exit the current function/script with an optional value
function Get-Grade ($score) {
if ($score -ge 90) { return "A" }
if ($score -ge 80) { return "B" }
if ($score -ge 70) { return "C" }
return "F"
}
Labeled break (exit nested loops)¶
Try / Catch / Finally¶
See the Error Handling page for full coverage. Quick reference:
try {
$result = 1 / 0
} catch [System.DivideByZeroException] {
"Cannot divide by zero"
} catch {
"Unexpected error: $_"
} finally {
"This always runs"
}
Practical Examples¶
Retry logic¶
$maxRetries = 3
$attempt = 0
$succeeded = $false
while (-not $succeeded -and $attempt -lt $maxRetries) {
$attempt++
try {
Invoke-RestMethod -Uri 'https://api.example.com/data'
$succeeded = $true
} catch {
Write-Warning "Attempt $attempt failed: $_"
Start-Sleep -Seconds (2 * $attempt) # exponential backoff
}
}
if (-not $succeeded) {
throw "All $maxRetries attempts failed"
}
Process a batch of servers¶
$servers = Import-Csv .\servers.csv
foreach ($server in $servers) {
if (-not (Test-Connection $server.Hostname -Count 1 -Quiet)) {
Write-Warning "$($server.Hostname) is offline — skipping"
continue
}
Invoke-Command -ComputerName $server.Hostname -ScriptBlock {
Get-Service | Where-Object Status -eq Stopped
}
}