Skip to content

PowerShell Loop Through Ranges

How to use PowerShell's range operator (..) with foreach and ForEach-Object for efficient numeric iteration, batch processing, and reverse counting.

The range operator (..) generates an ordered array of contiguous integers between a start and end boundary, inclusive. Combined with foreach or ForEach-Object, numeric ranges provide a clean, declarative alternative to classic index-based for loops for sequential iterations, batch operations, countdown routines, and more.

The Range Operator (..)

Syntax: <start>..<end>

1..10
1
2
3
4
5
6
7
8
9
10

The result is a genuine array, so it supports .Count, indexing, and piping just like any other array.

(1..10).Count
10

Ascending, Negative, and Descending Ranges

PowerShell automatically determines whether to increment or decrement based on the boundary values.

1..5

-3..2

10..1
1
2
3
4
5
-3
-2
-1
0
1
2
10
9
8
7
6
5
4
3
2
1

To verify the underlying .NET type generated by a range expression:

(1..5).GetType().FullName
(1..5)[0].GetType().FullName
System.Object[]
System.Int32

Looping Through Numeric Ranges

You can iterate across a range using either the foreach statement for high-speed in-memory loops or the ForEach-Object cmdlet for streaming pipeline workflows.

Using the foreach Statement

foreach ($i in 1..5) {
    Write-Output "Processing item $i of 5"
}
Processing item 1 of 5
Processing item 2 of 5
Processing item 3 of 5
Processing item 4 of 5
Processing item 5 of 5

Where:

  • 1..5 generates an array of five integers.
  • $i is assigned the next sequential integer on each iteration.

You can also iterate across practical ranges, such as a block of network ports:

foreach ($port in 8080..8083) {
    Write-Output "Auditing local listener on port: $port"
}
Auditing local listener on port: 8080
Auditing local listener on port: 8081
Auditing local listener on port: 8082
Auditing local listener on port: 8083

Using the Pipeline (ForEach-Object)

Piping a range expression into ForEach-Object streams integers down the pipeline one by one.

1..5 | ForEach-Object { Write-Output "Item $_" }
Item 1
Item 2
Item 3
Item 4
Item 5

Or with a batch-style message:

1..3 | ForEach-Object { "Processing batch item: $_" }
Processing batch item: 1
Processing batch item: 2
Processing batch item: 3

Reverse Ranges (10..1)

Reversing the two numbers in a range automatically generates a descending sequence. No special syntax or additional parameters are needed.

10..1
10
9
8
7
6
5
4
3
2
1

Countdown Script

foreach ($i in 10..1) {
    Write-Output "T-minus $i..."
    Start-Sleep -Seconds 1
}
Write-Output "Liftoff!"
T-minus 10...
T-minus 9...
T-minus 8...
T-minus 7...
T-minus 6...
T-minus 5...
T-minus 4...
T-minus 3...
T-minus 2...
T-minus 1...
Liftoff!

Where 10..1 detects that the starting boundary (10) is greater than the target boundary (1), so PowerShell automatically decrements by 1 on each step.

Practical Examples

Generating Sequential File Names

foreach ($i in 1..3) {
    $fileName = "report_{0:D3}.txt" -f $i
    New-Item -ItemType File -Path "C:\Temp\$fileName" -Force | Out-Null
    Write-Output "Created: $fileName"
}
Created: report_001.txt
Created: report_002.txt
Created: report_003.txt

Where {0:D3} formats the number with leading zeros to a total width of 3 digits.

Subnet IP Sweep

A common use case for range loops is generating sequential IP addresses across a subnet. The following script combines range iteration with string interpolation to scan a block of IP addresses and report reachability.

param (
    [string]$SubnetPrefix = "192.168.1",
    [int]$StartHost       = 10,
    [int]$EndHost         = 13
)

if ($StartHost -gt $EndHost) {
    Write-Warning "Starting host is greater than ending host. Executing reverse sweep."
}

$SweepResults = foreach ($octet in $StartHost..$EndHost) {
    $targetIP = "$SubnetPrefix.$octet"

    $isReachable = Test-Connection -ComputerName $targetIP -Count 1 -Quiet -ErrorAction SilentlyContinue

    [PSCustomObject]@{
        HostAddress = $targetIP
        HostOctet   = $octet
        Status      = if ($isReachable) { "ONLINE" } else { "UNRESPONSIVE" }
        CheckedAt   = (Get-Date -Format "HH:mm:ss")
    }
}

$SweepResults | Format-Table -AutoSize

Where:

  • $StartHost..$EndHost dynamically creates a range array based on user parameters.
  • "$SubnetPrefix.$octet" constructs the full IPv4 address via string interpolation.
  • Test-Connection -ComputerName $targetIP -Count 1 -Quiet sends a single ICMP ping and returns $true or $false.
  • $SweepResults = foreach (...) captures all emitted custom objects directly into an array.

Example run:

pwsh ./scan-ip-range.ps1 -SubnetPrefix "127.0.0" -StartHost 1 -EndHost 3
HostAddress HostOctet Status CheckedAt
----------- --------- ------ ---------
127.0.0.1           1 ONLINE 23:10:15
127.0.0.2           2 ONLINE 23:10:15
127.0.0.3           3 ONLINE 23:10:15

Quick Reference

Expression Result
1..5 1 2 3 4 5
5..1 5 4 3 2 1
-3..3 -3 -2 -1 0 1 2 3
(1..10).Count 10
1..10 | Where-Object { $_ % 2 -eq 0 } 2 4 6 8 10
'A'..'E' A B C D E (PowerShell 6+)
Navigation