Skip to content

PowerShell While Loop

How to use PowerShell's while loop for conditional iteration, including syntax, common use cases like polling services, input validation, and processing queues, plus best practices to avoid infinite loops.

The while loop is an entry-condition control flow structure that repeatedly executes a script block as long as a specified conditional expression evaluates to $true. It's the go-to loop construct when the number of iterations isn't known in advance and instead depends on a changing condition.

Because the conditional test occurs before the code block executes, a while loop executes zero times if the condition evaluates to $false at the start.

Basic while Syntax

The while loop consists of an evaluation condition enclosed in parentheses followed by an executable script block enclosed in curly braces.

Syntax Structure

while (<condition>) {
}

PowerShell evaluates <condition> as a boolean state. The loop continues to run as long as the expression produces $true (or a truthy value like a non-zero integer or populated object). As soon as the condition evaluates to $false or $null, execution exits the loop and continues with the next statement in the script.

while-basic.ps1powershell
$count = 1

while ($count -le 5) {
    Write-Output "Count is: $count"
    $count++
}

where:

  • $count = 1 initializes the loop control variable.
  • while ($count -le 5) evaluates the condition before each iteration; the loop runs only while this condition is $true.
  • Write-Output sends the iteration message to the pipeline.
  • $count++ operation increments the counter. Without it, the condition would never evaluate to false, resulting in an infinite loop.
Consolepowershell
./while-basic.ps1
Outputplaintext
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5

Common Use Cases

The while loop is best suited for scenarios where the exact number of iterations is unknown in advance and depends on external runtime conditions.

1. Waiting for a Condition to Become True (Polling System States)

while loops are commonly used to poll for a condition, such as waiting for a service to reach a running state:

wait-for-service.ps1powershell
$service = Get-Service -Name "Spooler"

while ($service.Status -ne "Running") {
    Write-Output "Waiting for Spooler service to start..."
    Start-Sleep -Seconds 2
    $service.Refresh()
}

Write-Output "Spooler service is now running."

where:

  • Start-Sleep -Seconds 2 pauses execution briefly between checks, avoiding excessive CPU usage from rapid polling.
  • .Refresh() updates the $service object with the service's current live status on each iteration.

2. Reading Input Until a Valid Value Is Given

validate-input.ps1powershell
$input = ""

while ($input -notmatch "^\d+$") {
    $input = Read-Host "Enter a number"
    if ($input -notmatch "^\d+$") {
        Write-Warning "That's not a valid number. Try again."
    }
}

Write-Output "You entered: $input"
Consolepowershell
./validate-input.ps1
Outputplaintext
Enter a number: abc
WARNING: That's not a valid number. Try again.
Enter a number: 42
You entered: 42

3. Processing Items Until a Queue Is Empty

A while loop is the standard pattern for draining collections, such as .NET queues (System.Collections.Generic.Queue[T]) or arrays, until empty:

process-queue.ps1powershell
$queue = [System.Collections.Generic.Queue[string]]::new()
$queue.Enqueue("Task1")
$queue.Enqueue("Task2")
$queue.Enqueue("Task3")

while ($queue.Count -gt 0) {
    $task = $queue.Dequeue()
    Write-Output "Processing: $task"
}
Outputplaintext
Processing: Task1
Processing: Task2
Processing: Task3

where:

  • $queue.Count -gt 0 keeps the loop running as long as items remain.
  • .Dequeue() removes and returns the next item, naturally shrinking the queue toward the exit condition.

4. Continuous Background Monitoring (Daemon Pattern)

Scripts designed to run continuously as background services use while ($true) with an internal Start-Sleep pause and explicit exit conditions (break):

monitor-daemon.ps1powershell
while ($true) {
    if (Test-Path -Path "C:\Configs\stop.signal") {
        Write-Output "Stop signal detected. Shutting down daemon."
        break
    }

    Start-Sleep -Seconds 10
}

Practical Example: Resilient Service Polling with Timeout

In production automation, polling an external resource indefinitely can cause scripts to freeze if the target never responds. The following script combines state evaluation with a timeout mechanism using a .NET Stopwatch object to ensure the loop terminates predictably:

wait-for-service-ready.ps1powershell
param (
    [string]$ServiceName = "Spooler",
    [int]$TimeoutSeconds = 10
)

$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$ServiceState = (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue).Status

Write-Output "Polling service '$ServiceName' (Timeout: $TimeoutSeconds seconds)..."

while ($ServiceState -ne "Running" -and $Stopwatch.Elapsed.TotalSeconds -lt $TimeoutSeconds) {
    Write-Output "Status: $ServiceState. Waiting for service to start..."
    Start-Sleep -Seconds 2

    $ServiceState = (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue).Status
}

$Stopwatch.Stop()

[PSCustomObject]@{
    ServiceName    = $ServiceName
    FinalStatus    = $ServiceState
    ElapsedSeconds = [math]::Round($Stopwatch.Elapsed.TotalSeconds, 2)
    IsOperational  = ($ServiceState -eq "Running")
    TimedOut       = ($ServiceState -ne "Running" -and $Stopwatch.Elapsed.TotalSeconds -ge $TimeoutSeconds)
}

where:

  • param(...) defines configurable parameters for the target service and timeout limit.
  • [System.Diagnostics.Stopwatch]::StartNew() begins high-resolution time tracking.
  • $ServiceState -ne "Running" -and $Stopwatch.Elapsed.TotalSeconds -lt $TimeoutSeconds forms the compound loop condition. The loop terminates if the service enters the Running state or if elapsed time exceeds $TimeoutSeconds.
  • Start-Sleep -Seconds 2 pauses execution to avoid pegging the CPU during polling cycles.
  • [PSCustomObject] summarizes the outcome into a structured data object.

Execute the script from the console:

Consolepowershell
pwsh ./wait-for-service-ready.ps1 -ServiceName "Spooler" -TimeoutSeconds 6
Outputplaintext
Polling service 'Spooler' (Timeout: 10 seconds)...


ServiceName    : Spooler
FinalStatus    : Running
ElapsedSeconds : 0.02
IsOperational  : True
TimedOut       : False
Navigation