Skip to main content

PowerShell For Loops: Complete Guide with Examples [2024]

10 min read
powershell for loop loops scripting windows iteration control-flow

The for loop in PowerShell is a fundamental control flow structure that allows you to execute a block of code repeatedly based on a counter. It’s one of the most efficient ways to iterate a specific number of times or process indexed collections.

In this comprehensive guide, we’ll cover everything you need to know about PowerShell for loops, from basic syntax to advanced techniques with real-world examples.

Table of Contents

What is a For Loop in PowerShell? {#what-is-a-for-loop}

A for loop is a control flow statement that executes a block of code a specific number of times based on a counter variable. Unlike foreach loops that iterate over collections, for loops give you precise control over the iteration process with initialization, condition checking, and increment/decrement operations.

Key Characteristics:

  • Counter-based iteration (not collection-based)
  • Precise control over start, end, and step values
  • Efficient for indexed access to arrays
  • Similar syntax to C, C++, Java, JavaScript
  • Supports break and continue statements

Why Use For Loops? {#why-use-for-loops}

For loops are ideal when you need to:

  • Execute code a specific number of times
  • Access array elements by index position
  • Iterate with custom increment/decrement values
  • Process items in reverse order
  • Implement complex iteration logic with nested loops
  • Optimize performance for large indexed collections

When NOT to use for loops:

  • When iterating over all elements in a collection without needing the index (use foreach instead)
  • When processing pipeline objects (use ForEach-Object instead)
  • When iteration count is unknown (use while or do-while instead)

For Loop Syntax {#syntax}

The syntax of the for loop in PowerShell follows this structure:

for (<initialization>; <condition>; <iteration>) {
    # Code to execute in each iteration
}

Components:

  • Initialization: Executed once before the loop starts (typically sets counter variable)
  • Condition: Evaluated before each iteration; loop continues while true
  • Iteration: Executed after each iteration (typically increments/decrements counter)
  • Code Block: Statements executed on each iteration

Example breakdown:

for ($i = 0; $i -lt 10; $i++) {
    Write-Host "Count: $i"
}
# $i = 0        → Initialize counter to 0
# $i -lt 10     → Continue while $i is less than 10
# $i++          → Increment $i by 1 after each iteration

Basic For Loop Examples {#basic-examples}

Simple Counting Loop

for ($i = 1; $i -le 5; $i++) {
    Write-Host "Iteration $i"
}

Output:

Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5

Explanation: This loop initializes $i to 1, runs while $i is less than or equal to 5, and increments $i by 1 after each iteration.

Starting from Zero

for ($i = 0; $i -lt 5; $i++) {
    Write-Host "Index: $i"
}

Output:

Index: 0
Index: 1
Index: 2
Index: 3
Index: 4

Note: Starting from 0 is common when working with arrays since PowerShell arrays are zero-indexed.

Counting Backward (Reverse) {#counting-backward}

Use the decrement operator ($i--) to count backward:

for ($i = 5; $i -ge 1; $i--) {
    Write-Host "Countdown: $i"
}

Output:

Countdown: 5
Countdown: 4
Countdown: 3
Countdown: 2
Countdown: 1

Real-world example - Processing files in reverse:

$files = Get-ChildItem -Path "C:\Logs" -File

for ($i = $files.Count - 1; $i -ge 0; $i--) {
    Write-Host "Processing: $($files[$i].Name)"
}

Custom Increment and Decrement {#custom-increment}

Increment by Custom Value

# Count by 2s
for ($i = 0; $i -le 10; $i += 2) {
    Write-Host "Even number: $i"
}

Output:

Even number: 0
Even number: 2
Even number: 4
Even number: 6
Even number: 8
Even number: 10

Increment by 5s

for ($i = 0; $i -le 50; $i += 5) {
    Write-Host "Multiple of 5: $i"
}

Decrement by Custom Value

# Count down by 10s
for ($i = 100; $i -ge 0; $i -= 10) {
    Write-Host "Value: $i"
}

Output:

Value: 100
Value: 90
Value: 80
...
Value: 10
Value: 0

For Loops with Arrays {#for-loops-arrays}

Accessing Array Elements by Index {#array-index}

$languages = @("PowerShell", "Python", "C#", "JavaScript", "Go")

for ($i = 0; $i -lt $languages.Length; $i++) {
    Write-Host "[$i] $($languages[$i])"
}

Output:

[0] PowerShell
[1] Python
[2] C#
[3] JavaScript
[4] Go

Why use for instead of foreach?

  • You need the index position
  • You want to modify array elements
  • You need to process only specific indices
  • You want to iterate in reverse

Modifying Array Elements {#modify-arrays}

$numbers = @(1, 2, 3, 4, 5)

# Double each number
for ($i = 0; $i -lt $numbers.Length; $i++) {
    $numbers[$i] = $numbers[$i] * 2
}

Write-Host "Modified array: $($numbers -join ', ')"

Output:

Modified array: 2, 4, 6, 8, 10

Note: You cannot modify array elements directly in a foreach loop, but you can with a for loop.

Processing Every Nth Element

$data = 1..100

# Process every 10th element
for ($i = 0; $i -lt $data.Length; $i += 10) {
    Write-Host "Element at index $i : $($data[$i])"
}

Iterating Array in Reverse

$servers = @("Server01", "Server02", "Server03", "Server04")

# Process servers in reverse order
for ($i = $servers.Count - 1; $i -ge 0; $i--) {
    Write-Host "Processing: $($servers[$i])"
}

Output:

Processing: Server04
Processing: Server03
Processing: Server02
Processing: Server01

For Loops with Hashtables {#for-loops-hashtables}

While hashtables are typically iterated with foreach, you can use for loops with their keys or values:

$users = @{
    "JDoe" = "John Doe"
    "JSmith" = "Jane Smith"
    "BWhite" = "Bob White"
}

$keys = $users.Keys
for ($i = 0; $i -lt $keys.Count; $i++) {
    $key = $keys[$i]
    Write-Host "$key = $($users[$key])"
}

Note: For hashtables, foreach is usually more appropriate. Use for loops only when you need index-based access.

Nested For Loops {#nested-loops}

Nested for loops allow you to create multi-dimensional iterations:

Simple Nested Loop

for ($i = 1; $i -le 3; $i++) {
    for ($j = 1; $j -le 3; $j++) {
        Write-Host "i=$i, j=$j"
    }
}

Output:

i=1, j=1
i=1, j=2
i=1, j=3
i=2, j=1
i=2, j=2
i=2, j=3
i=3, j=1
i=3, j=2
i=3, j=3

Multiplication Table

# Generate a 5x5 multiplication table
for ($i = 1; $i -le 5; $i++) {
    $row = ""
    for ($j = 1; $j -le 5; $j++) {
        $row += "{0,4}" -f ($i * $j)
    }
    Write-Host $row
}

Output:

   1   2   3   4   5
   2   4   6   8  10
   3   6   9  12  15
   4   8  12  16  20
   5  10  15  20  25

Processing Multi-Dimensional Arrays

$matrix = @(
    @(1, 2, 3),
    @(4, 5, 6),
    @(7, 8, 9)
)

for ($i = 0; $i -lt $matrix.Count; $i++) {
    for ($j = 0; $j -lt $matrix[$i].Count; $j++) {
        Write-Host "[$i][$j] = $($matrix[$i][$j])"
    }
}

Break Statement in For Loops {#break-statement}

The break statement immediately exits the loop:

for ($i = 1; $i -le 10; $i++) {
    if ($i -eq 5) {
        Write-Host "Breaking at $i"
        break
    }
    Write-Host "Iteration: $i"
}
Write-Host "Loop ended"

Output:

Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Breaking at 5
Loop ended

Real-world example - Stop processing on error:

$files = Get-ChildItem -Path "C:\Data" -File

for ($i = 0; $i -lt $files.Count; $i++) {
    try {
        $content = Get-Content $files[$i].FullName -ErrorAction Stop
        # Process content...
    }
    catch {
        Write-Host "Error processing $($files[$i].Name): $_"
        break  # Stop processing on first error
    }
}

Break in Nested Loops

$found = $false
for ($i = 1; $i -le 5; $i++) {
    for ($j = 1; $j -le 5; $j++) {
        if ($i * $j -eq 12) {
            Write-Host "Found: $i * $j = 12"
            $found = $true
            break  # Exits inner loop only
        }
    }
    if ($found) { break }  # Exits outer loop
}

Continue Statement in For Loops {#continue-statement}

The continue statement skips the current iteration and moves to the next:

for ($i = 1; $i -le 10; $i++) {
    if ($i % 2 -eq 0) {
        continue  # Skip even numbers
    }
    Write-Host "Odd number: $i"
}

Output:

Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9

Real-world example - Skip failed items:

$servers = @("Server01", "Server02", "Server03", "Server04")

for ($i = 0; $i -lt $servers.Count; $i++) {
    if (-not (Test-Connection $servers[$i] -Count 1 -Quiet)) {
        Write-Host "$($servers[$i]) is offline, skipping..."
        continue
    }

    # Process online server
    Write-Host "Processing $($servers[$i])..."
}

Infinite For Loops {#infinite-loops}

An infinite loop runs indefinitely until explicitly broken:

# Infinite loop (use with caution!)
for (;;) {
    Write-Host "This runs forever..."
    Start-Sleep -Seconds 1

    # You MUST have a break condition
    if ($someCondition) {
        break
    }
}

Practical example - Monitoring with infinite loop:

$maxAttempts = 100
$attempts = 0

for (;;) {
    $attempts++

    $status = Get-Service "MyService"

    if ($status.Status -eq "Running") {
        Write-Host "Service is running!"
        break
    }

    if ($attempts -ge $maxAttempts) {
        Write-Host "Max attempts reached"
        break
    }

    Write-Host "Waiting for service to start... (Attempt $attempts)"
    Start-Sleep -Seconds 5
}

⚠️ Warning: Always ensure infinite loops have a break condition to prevent runaway scripts.

For vs ForEach vs ForEach-Object vs While {#comparison}

Comparison Table

Loop TypeBest ForSyntaxIndex AccessModify Array
forCounter-based, indexed accessfor ($i=0; $i -lt 10; $i++)✅ Yes✅ Yes
foreachIterating all collection itemsforeach ($item in $collection)❌ No❌ No
ForEach-ObjectPipeline processing$collection | ForEach-Object {}❌ No❌ No
whileCondition-based iterationwhile ($condition) {}➖ Manual➖ Manual

When to Use Each

Use for when:

  • You need the index position
  • You want to modify array elements
  • You need custom increment/decrement
  • You want to iterate in reverse
  • Performance is critical for large indexed collections

Use foreach when:

  • Iterating over all items without needing index
  • Code readability is priority
  • Simple iteration without modifications

Use ForEach-Object when:

  • Processing pipeline input
  • Working with cmdlet output
  • Need to process items as they arrive

Use while when:

  • Iteration count is unknown
  • Looping based on dynamic condition
  • Implementing retry logic

Performance Example

$data = 1..10000

# Method 1: for loop
$sw = [System.Diagnostics.Stopwatch]::StartNew()
for ($i = 0; $i -lt $data.Count; $i++) {
    $result = $data[$i] * 2
}
$sw.Stop()
Write-Host "for loop: $($sw.ElapsedMilliseconds)ms"

# Method 2: foreach loop
$sw.Restart()
foreach ($item in $data) {
    $result = $item * 2
}
$sw.Stop()
Write-Host "foreach loop: $($sw.ElapsedMilliseconds)ms"

# Method 3: ForEach-Object
$sw.Restart()
$data | ForEach-Object {
    $result = $_ * 2
}
$sw.Stop()
Write-Host "ForEach-Object: $($sw.ElapsedMilliseconds)ms"

Typical Results:

for loop: 5ms
foreach loop: 4ms
ForEach-Object: 45ms

Performance Ranking: foreachfor > ForEach-Object (for-object is ~10x slower for large collections)

Performance Comparison {#performance}

Optimization Tips

1. Use for loops for indexed access (faster than Where-Object):

# Slower
$filtered = $largeArray | Where-Object { $_ -gt 50 }

# Faster
$filtered = @()
for ($i = 0; $i -lt $largeArray.Count; $i++) {
    if ($largeArray[$i] -gt 50) {
        $filtered += $largeArray[$i]
    }
}

2. Pre-allocate arrays when possible:

# Slower (dynamic resizing)
$results = @()
for ($i = 0; $i -lt 1000; $i++) {
    $results += $i * 2
}

# Faster (pre-allocated)
$results = New-Object 'System.Collections.Generic.List[int]'
for ($i = 0; $i -lt 1000; $i++) {
    $results.Add($i * 2)
}

3. Avoid pipeline in performance-critical loops:

# Slower
for ($i = 0; $i -lt $items.Count; $i++) {
    $items[$i] | Some-Cmdlet | Another-Cmdlet
}

# Faster
for ($i = 0; $i -lt $items.Count; $i++) {
    $result = Some-Cmdlet $items[$i]
    $final = Another-Cmdlet $result
}

Real-World Use Cases {#use-cases}

1. Batch Processing Files {#batch-files}

# Process files in batches of 10
$files = Get-ChildItem -Path "C:\Data" -File
$batchSize = 10

for ($i = 0; $i -lt $files.Count; $i += $batchSize) {
    $batch = $files[$i..([Math]::Min($i + $batchSize - 1, $files.Count - 1))]

    Write-Host "Processing batch $([Math]::Floor($i / $batchSize) + 1)..."

    foreach ($file in $batch) {
        # Process each file in the batch
        $content = Get-Content $file.FullName
        # Do something with content...
    }

    # Optional: Add delay between batches
    Start-Sleep -Seconds 2
}

2. Creating Bulk AD Users {#bulk-users}

# Create 100 test user accounts
$ouPath = "OU=TestUsers,DC=domain,DC=com"

for ($i = 1; $i -le 100; $i++) {
    $username = "TestUser{0:D3}" -f $i  # TestUser001, TestUser002, etc.

    $params = @{
        Name              = $username
        SamAccountName    = $username
        UserPrincipalName = "$username@domain.com"
        Path              = $ouPath
        AccountPassword   = (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force)
        Enabled           = $true
    }

    try {
        New-ADUser @params
        Write-Host "Created user: $username" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed to create $username : $_" -ForegroundColor Red
    }
}

3. Processing Log Files with Progress {#log-files}

# Analyze log files line by line with progress indicator
$logFile = "C:\Logs\application.log"
$lines = Get-Content $logFile
$errorCount = 0

for ($i = 0; $i -lt $lines.Count; $i++) {
    # Show progress every 1000 lines
    if ($i % 1000 -eq 0) {
        $percent = [Math]::Round(($i / $lines.Count) * 100, 2)
        Write-Host "Progress: $percent% ($i / $($lines.Count) lines)"
    }

    # Check for errors
    if ($lines[$i] -match "ERROR|EXCEPTION") {
        $errorCount++
        Write-Host "Line $($i+1): $($lines[$i])" -ForegroundColor Red
    }
}

Write-Host "`nTotal errors found: $errorCount"

4. Generating Reports with Pagination {#reports}

# Generate paginated report
$data = Get-ADUser -Filter * -Properties Department, Title
$pageSize = 50
$pageCount = [Math]::Ceiling($data.Count / $pageSize)

for ($page = 0; $page -lt $pageCount; $page++) {
    $startIndex = $page * $pageSize
    $endIndex = [Math]::Min($startIndex + $pageSize - 1, $data.Count - 1)

    $pageData = $data[$startIndex..$endIndex]

    $reportFile = "C:\Reports\Users_Page_$($page + 1).html"

    $html = $pageData | ConvertTo-Html -Property Name, Department, Title
    $html | Out-File $reportFile

    Write-Host "Generated page $($page + 1) of $pageCount : $reportFile"
}

5. Retry Logic with Exponential Backoff {#retry-logic}

# Retry operation with exponential backoff
$maxRetries = 5
$baseDelay = 2  # seconds

for ($attempt = 1; $attempt -le $maxRetries; $attempt++) {
    try {
        # Attempt the operation
        $result = Invoke-WebRequest -Uri "https://api.example.com/data" -ErrorAction Stop

        Write-Host "Request succeeded on attempt $attempt"
        break
    }
    catch {
        if ($attempt -eq $maxRetries) {
            Write-Host "Max retries reached. Operation failed." -ForegroundColor Red
            throw
        }

        # Calculate exponential backoff delay: 2^attempt seconds
        $delay = [Math]::Pow(2, $attempt)
        Write-Host "Attempt $attempt failed. Retrying in $delay seconds..."
        Start-Sleep -Seconds $delay
    }
}

Common Mistakes {#common-mistakes}

1. Off-by-One Errors

❌ Wrong:

$array = @(1, 2, 3, 4, 5)
for ($i = 0; $i -le $array.Length; $i++) {
    Write-Host $array[$i]  # Error: Index out of range
}

✅ Correct:

for ($i = 0; $i -lt $array.Length; $i++) {
    Write-Host $array[$i]
}

2. Modifying Array in foreach Instead of for

❌ Wrong:

$numbers = @(1, 2, 3, 4, 5)
foreach ($num in $numbers) {
    $num = $num * 2  # This doesn't modify the array!
}

✅ Correct:

for ($i = 0; $i -lt $numbers.Count; $i++) {
    $numbers[$i] = $numbers[$i] * 2
}

3. Forgetting to Initialize Counter

❌ Wrong:

for (; $i -lt 10; $i++) {  # $i is not initialized
    Write-Host $i
}

✅ Correct:

for ($i = 0; $i -lt 10; $i++) {
    Write-Host $i
}

4. Infinite Loop Without Break Condition

❌ Wrong:

for (;;) {
    Write-Host "Running..."
    # No break condition - runs forever!
}

✅ Correct:

$maxIterations = 100
$count = 0
for (;;) {
    Write-Host "Running..."
    $count++
    if ($count -ge $maxIterations) { break }
}

5. Using Wrong Comparison Operator

❌ Wrong:

for ($i = 5; $i -le 1; $i--) {  # Condition is already false!
    Write-Host $i
}

✅ Correct:

for ($i = 5; $i -ge 1; $i--) {
    Write-Host $i
}

6. Modifying Counter Inside Loop Body

❌ Wrong:

for ($i = 0; $i -lt 10; $i++) {
    Write-Host $i
    $i += 2  # Confusing - counter modified in two places
}

✅ Correct:

for ($i = 0; $i -lt 10; $i += 3) {
    Write-Host $i
}

7. Not Handling Empty Arrays

❌ Wrong:

$files = Get-ChildItem -Path "C:\EmptyFolder"
for ($i = 0; $i -lt $files.Count; $i++) {
    Write-Host $files[$i].Name  # Error if folder is empty
}

✅ Correct:

$files = Get-ChildItem -Path "C:\EmptyFolder"
if ($files.Count -gt 0) {
    for ($i = 0; $i -lt $files.Count; $i++) {
        Write-Host $files[$i].Name
    }
} else {
    Write-Host "No files found"
}

Best Practices {#best-practices}

1. Use Descriptive Variable Names

❌ Avoid:

for ($x = 0; $x -lt $y.Count; $x++) {
    Write-Host $y[$x]
}

✅ Prefer:

for ($index = 0; $index -lt $servers.Count; $index++) {
    Write-Host $servers[$index]
}

2. Choose the Right Loop Type

Use for when you need index access, otherwise prefer foreach for readability:

# Good: for loop when index is needed
for ($i = 0; $i -lt $files.Count; $i++) {
    Write-Host "[$i] $($files[$i].Name)"
}

# Better: foreach when index is not needed
foreach ($file in $files) {
    Write-Host $file.Name
}

3. Keep Loop Conditions Simple

❌ Complex:

for ($i = 0; $i -lt (Get-ChildItem).Count -and $errorCount -lt 5; $i++) {
    # Hard to read
}

✅ Simple:

$files = Get-ChildItem
for ($i = 0; $i -lt $files.Count; $i++) {
    if ($errorCount -ge 5) { break }
    # Code here
}

4. Avoid Deeply Nested Loops

Limit nesting to 2-3 levels for readability. Extract inner logic to functions if needed:

function Process-Item {
    param($item)
    # Inner loop logic here
}

for ($i = 0; $i -lt $data.Count; $i++) {
    Process-Item -item $data[$i]
}

5. Use .Count or .Length Appropriately

# For arrays: .Count or .Length (both work)
for ($i = 0; $i -lt $array.Count; $i++) { }

# For collections: .Count
for ($i = 0; $i -lt $collection.Count; $i++) { }

# For strings: .Length
for ($i = 0; $i -lt $string.Length; $i++) { }

6. Add Progress Indicators for Long Operations

$total = $items.Count
for ($i = 0; $i -lt $total; $i++) {
    Write-Progress -Activity "Processing Items" `
                   -Status "$i of $total" `
                   -PercentComplete (($i / $total) * 100)

    # Process item...
}
Write-Progress -Activity "Processing Items" -Completed

7. Use Try-Catch in Loops

for ($i = 0; $i -lt $files.Count; $i++) {
    try {
        # Risky operation
        Remove-Item $files[$i].FullName -ErrorAction Stop
    }
    catch {
        Write-Host "Error deleting $($files[$i].Name): $_" -ForegroundColor Red
        # Decide: continue or break
    }
}

Troubleshooting {#troubleshooting}

Issue 1: “Index was outside the bounds of the array”

Cause: Accessing an index that doesn’t exist

Solution:

# Check array bounds
if ($i -lt $array.Count) {
    $value = $array[$i]
}

# Or use -lt instead of -le in loop condition
for ($i = 0; $i -lt $array.Count; $i++) {  # Not -le!
    Write-Host $array[$i]
}

Issue 2: Loop Runs Forever

Cause: Condition never becomes false, or counter not incrementing

Solution:

# Add debug output
for ($i = 0; $i -lt 10; $i++) {
    Write-Host "Debug: i = $i"
    # Your code
}

# Or add max iteration limit
$maxIterations = 1000
for ($i = 0; $i -lt 10 -and $i -lt $maxIterations; $i++) {
    # Your code
}

Issue 3: Counter Not Incrementing Correctly

Cause: Incorrect increment statement or modifying counter in loop body

Solution:

# Make sure increment is correct
for ($i = 0; $i -lt 10; $i++) {  # Not $i--
    Write-Host $i
}

# Avoid modifying counter inside loop

Issue 4: Performance Issues with Large Collections

Cause: Inefficient operations inside loop

Solution:

# Move invariant operations outside loop
$pattern = "ERROR"  # Don't redefine this inside loop
for ($i = 0; $i -lt $lines.Count; $i++) {
    if ($lines[$i] -match $pattern) {
        # Process
    }
}

# Use efficient collections
$results = [System.Collections.ArrayList]@()
for ($i = 0; $i -lt 10000; $i++) {
    $null = $results.Add($i)  # Much faster than +=
}

FAQs {#faqs}

Q1: What’s the difference between for and foreach loops?

A: for loops use a counter and give you index access, while foreach loops iterate directly over collection items without an index. Use for when you need the position, use foreach for simpler iteration.

Q2: Can I modify an array inside a for loop?

A: Yes! That’s one of the main advantages of for loops:

for ($i = 0; $i -lt $array.Count; $i++) {
    $array[$i] = $array[$i] * 2  # This works!
}

Q3: How do I exit a for loop early?

A: Use the break statement:

for ($i = 0; $i -lt 100; $i++) {
    if ($someCondition) {
        break  # Exits the loop immediately
    }
}

Q4: How do I skip an iteration in a for loop?

A: Use the continue statement:

for ($i = 0; $i -lt 10; $i++) {
    if ($i % 2 -eq 0) {
        continue  # Skips to next iteration
    }
    Write-Host $i  # Only prints odd numbers
}

Q5: Can I have an empty initialization or increment?

A: Yes, all three parts of a for loop are optional:

$i = 0  # Initialize outside
for (; $i -lt 10;) {
    Write-Host $i
    $i++  # Increment inside
}

Q6: How do I create an infinite loop?

A: Use for (;;) but always include a break condition:

for (;;) {
    # Your code
    if ($exitCondition) { break }
}

Q7: Can I iterate over a range of numbers?

A: Yes, but using a range operator with foreach is simpler:

# Using for loop
for ($i = 1; $i -le 10; $i++) {
    Write-Host $i
}

# Using range with foreach (simpler)
foreach ($i in 1..10) {
    Write-Host $i
}

Q8: How do I iterate backward through an array?

A: Start from the last index and decrement:

for ($i = $array.Count - 1; $i -ge 0; $i--) {
    Write-Host $array[$i]
}

Q9: Can I use multiple counters in a for loop?

A: Not directly in the syntax, but you can manage multiple variables:

$j = 100
for ($i = 0; $i -lt 10; $i++) {
    Write-Host "i=$i, j=$j"
    $j -= 10
}

Q10: Which is faster: for or foreach?

A: For large arrays, foreach is slightly faster (~5-10%), but both are much faster than ForEach-Object. Use for when you need index access, foreach otherwise.

Q11: How do I process an array in chunks?

A: Use a for loop with custom increment:

$chunkSize = 10
for ($i = 0; $i -lt $array.Count; $i += $chunkSize) {
    $chunk = $array[$i..([Math]::Min($i + $chunkSize - 1, $array.Count - 1))]
    # Process chunk
}

Q12: Can I nest for loops?

A: Yes, you can nest as many levels as needed (though 2-3 is recommended for readability):

for ($i = 0; $i -lt $rows; $i++) {
    for ($j = 0; $j -lt $cols; $j++) {
        Write-Host "[$i][$j] = $($matrix[$i][$j])"
    }
}

Q13: How do I handle an empty array in a for loop?

A: Check the count before looping:

if ($array.Count -gt 0) {
    for ($i = 0; $i -lt $array.Count; $i++) {
        # Process
    }
}

Q14: What happens if I don’t increment the counter?

A: The loop will run infinitely because the condition never changes. Always ensure your counter is modified in the iteration statement or loop body.

Q15: Can I use for loops with custom objects?

A: Yes, if the objects are in an array:

$users = @(
    [PSCustomObject]@{Name="John"; Age=30},
    [PSCustomObject]@{Name="Jane"; Age=25}
)

for ($i = 0; $i -lt $users.Count; $i++) {
    Write-Host "$($users[$i].Name) is $($users[$i].Age) years old"
}

Conclusion {#conclusion}

PowerShell for loops are essential for counter-based iteration, providing precise control over loop execution with initialization, conditions, and increment operations. They excel when you need index access, want to modify array elements, or require custom iteration patterns.

Key Takeaways:

  • Use for loops when you need index positions or want to modify arrays
  • Use foreach for simpler collection iteration without index requirements
  • Always ensure loop conditions eventually become false to avoid infinite loops
  • Use break to exit loops early and continue to skip iterations
  • For loops offer similar performance to foreach loops for most operations
  • Nested loops are powerful but should be limited to 2-3 levels for readability

Next Steps:

  • Practice with real-world scenarios like batch processing and report generation
  • Explore nested loops for multi-dimensional data processing
  • Combine for loops with error handling using try-catch blocks
  • Optimize performance for large datasets using efficient collection types

For more control flow techniques, see our guides on PowerShell ForEach loops, If/Else statements, and While loops.

Control Flow & Logic

Loop Alternatives

Data Selection & Filtering

Variables & Collections

Display & Output

String Operations

System Operations & Administration

Functions & Code Organization

Comprehensive Guides