The for loop is an index-driven control flow structure that repeats a block of code a specific number of times. Commonly referred to as the "classic" or three-part for loop, it initializes a control variable, tests a conditional expression before each pass, and executes an increment or step statement after each iteration. It's the preferred choice when the number of iterations is known in advance and you need direct control over the counter variable.
The Classic for Loop Syntax
The for loop statement header consists of three sections separated by semicolons (;), followed by an executable script block:
Syntax Structure
for (<Init>; <Condition>; <Repeat>) {
}The loop operates in four sequential phases:
<Init>: Executes once before the loop starts. Typically used to declare and initialize a loop control variable (e.g.,$i = 0).<Condition>: Evaluated before each iteration. If it evaluates to$true, the loop body executes. If it evaluates to$false, the loop terminates.- Loop Body (
{ ... }): Executes the enclosed statements. <Repeat>: Executes after each iteration (e.g.,$i++), before returning to step 2 to re-evaluate the condition.
+------------------+
| 1. <Init> | (Runs once at startup)
+--------+---------+
|
v
+------------------+
+----->| 2. <Condition> |
| +--------+---------+
| |
| ($true) | ($false)
| +-----+-----+
| | |
| v v
| +------------+ +---------------------+
| | Loop Body | | Exit loop & proceed |
| +------+-----+ +---------------------+
| |
| v
| +------------+
+--+ 3. <Repeat>| (e.g., $i++)
+------------+for ($i = 0; $i -lt 5; $i++) {
Write-Output "Processing iteration index: $i"
}where:
$i = 0initializes the index variable$ito0.$i -lt 5evaluates whether$iis strictly less than5before entering the script block.Write-Outputemits the status string to the pipeline.$i++uses the postfix increment operator to add1to$iat the end of each pass.
pwsh ./for-basic-counter.ps1Processing iteration index: 0
Processing iteration index: 1
Processing iteration index: 2
Processing iteration index: 3
Processing iteration index: 4Custom Step Increments
The <Repeat> expression is not restricted to single-unit increments ($i++). You can use any assignment or arithmetic operator to adjust the step size or iterate in reverse.
for ($i = 0; $i -le 20; $i += 5) {
Write-Output "Value: $i"
}./for-step.ps1Value: 0
Value: 5
Value: 10
Value: 15
Value: 20To skip elements or process items in batches, use compound assignment operators like +=:
for ($port = 8000; $port -le 8040; $port += 10) {
Write-Output "Configuring listener on port: $port"
}pwsh ./for-step-increment.ps1Configuring listener on port: 8000
Configuring listener on port: 8010
Configuring listener on port: 8020
Configuring listener on port: 8030
Configuring listener on port: 8040Reverse Iteration and Counting Downward
Reversing the initializer, condition, and increment produces a descending loop.
for ($i = 5; $i -ge 1; $i--) {
Write-Output "T-minus $i"
}
Write-Output "Liftoff!"./for-countdown.ps1T-minus 5
T-minus 4
T-minus 3
T-minus 2
T-minus 1
Liftoff!To iterate backwards through an array or run a countdown, initialize the counter to the highest bound, check for a lower bound using -ge, and decrement using --:
for ($secondsRemaining = 3; $secondsRemaining -gt 0; $secondsRemaining--) {
Write-Output "Launch window initializes in: $secondsRemaining seconds..."
}
Write-Output "Window open."pwsh ./for-reverse-countdown.ps1Launch window initializes in: 3 seconds...
Launch window initializes in: 2 seconds...
Launch window initializes in: 1 seconds...
Window open.Iterating Over an Array by Index
Unlike foreach, a for loop gives direct access to the index, which is useful when you need to know an item's position or compare it with neighboring elements.
$servers = @("WEB01", "WEB02", "DB01", "DB02")
for ($i = 0; $i -lt $servers.Count; $i++) {
Write-Output "Server $($i + 1): $($servers[$i])"
}where:
$servers.Countdetermines the total number of iterations dynamically, based on the array's actual size.$servers[$i]accesses the array element at the current index.
./for-array-index.ps1Server 1: WEB01
Server 2: WEB02
Server 3: DB01
Server 4: DB02Nesting for Loops
for loops can be nested to iterate over multi-dimensional data, such as a grid or matrix.
for ($row = 0; $row -lt 3; $row++) {
for ($col = 0; $col -lt 3; $col++) {
Write-Output "Cell [$row,$col]"
}
}./for-nested.ps1Cell [0,0]
Cell [0,1]
Cell [0,2]
Cell [1,0]
Cell [1,1]
Cell [1,2]
Cell [2,0]
Cell [2,1]
Cell [2,2]Omitted Clauses and Infinite Loops
All three components of the for statement header are optional:
- Omitting
<Init>assumes the counter is already initialized earlier in the script. - Omitting
<Repeat>assumes the counter is updated manually inside the loop body. - Omitting
<Condition>defaults to$true.
Leaving all three clauses empty produces an infinite loop:
for (;;) {
break
}This syntax is functionally equivalent to while ($true), but while ($true) is generally preferred for script clarity.
Practical Example: Staged Batch Deployment with Progress Tracking
The classic for loop is ideal for iterating across collections when you need direct access to the numeric index to compute completion percentages or synchronize multiple parallel arrays:
param ()
$ClusterNodes = @("lon-web-01", "lon-web-02", "lon-web-03", "lon-web-04")
$TotalNodes = $ClusterNodes.Count
$Results = for ($index = 0; $index -lt $TotalNodes; $index++) {
$CurrentNode = $ClusterNodes[$index]
$PercentComplete = [math]::Round((($index + 1) / $TotalNodes) * 100, 1)
Start-Sleep -Milliseconds 100
[PSCustomObject]@{
StageIndex = $index + 1
NodeName = $CurrentNode
PercentComplete = "$PercentComplete%"
DeploymentState = "Provisioned"
Timestamp = (Get-Date -Format "HH:mm:ss.fff")
}
}
$Results | Format-Table -AutoSizewhere:
$TotalNodes = $ClusterNodes.Countdetermines the upper array bound.for ($index = 0; $index -lt $TotalNodes; $index++)tracks the array offset from0to3.$ClusterNodes[$index]uses the loop counter to retrieve the target hostname from the array.[math]::Round((($index + 1) / $TotalNodes) * 100, 1)calculates progress completion based on the current step.$Results = for (...) { ... }captures the objects emitted by every iteration directly into an array variable.
Execute the script from the console:
pwsh ./staged-node-deployment.ps1StageIndex NodeName PercentComplete DeploymentState Timestamp
---------- -------- --------------- --------------- ---------
1 lon-web-01 25% Provisioned 19:14:10.182
2 lon-web-02 50% Provisioned 19:14:10.307
3 lon-web-03 75% Provisioned 19:14:10.417
4 lon-web-04 100% Provisioned 19:14:10.528