Post-test loops evaluate their conditional expression at the bottom of the loop structure rather than the top. PowerShell provides two post-test looping constructs: do-while and do-until. Because the conditional evaluation occurs after the code block has already run, both constructs guarantee that the enclosed script block executes at least once, regardless of whether the initial condition is $true or $false.
Syntax: do-while vs. do-until
While both constructs share identical execution flow, they differ in how they evaluate their termination conditions:
do { } while (<condition>): Repeats the loop as long as the condition evaluates to$true. It exits when the condition becomes$false.do { } until (<condition>): Repeats the loop as long as the condition evaluates to$false. It exits as soon as the condition becomes$true.
Syntax Structure
do {
} while (<condition>)
do {
} until (<condition>)$counterWhile = 1
Write-Output "--- do-while demonstration ---"
do {
Write-Output "do-while count: $counterWhile"
$counterWhile++
} while ($counterWhile -le 3)
$counterUntil = 1
Write-Output "`n--- do-until demonstration ---"
do {
Write-Output "do-until count: $counterUntil"
$counterUntil++
} until ($counterUntil -gt 3)where:
while ($counterWhile -le 3)keeps repeating while the counter is less than or equal to3.until ($counterUntil -gt 3)keeps repeating until the counter exceeds3.- In both cases, the loops produce identical iteration counts using opposite conditional logic.
Execute the script from the console:
pwsh ./dowhile-vs-dountil.ps1--- do-while demonstration ---
do-while count: 1
do-while count: 2
do-while count: 3
--- do-until demonstration ---
do-until count: 1
do-until count: 2
do-until count: 3Basic do-while Syntax
$count = 1
do {
Write-Output "Count is: $count"
$count++
} while ($count -le 5)where:
- The code block runs first, then
while ($count -le 5)is evaluated. - If the condition is
$true, the loop repeats; if$false, the loop ends.
./do-while-basic.ps1Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5Basic do-until Syntax
do-until functions the same way as do-while, with one key difference: the loop keeps executing until the condition evaluates to $true, essentially reversing the logic.
$count = 1
do {
Write-Output "Count is: $count"
$count++
} until ($count -gt 5)where until ($count -gt 5) keeps the loop running while the condition is $false, stopping as soon as it becomes $true.
./do-until-basic.ps1Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5Key Differentiator: Guaranteed Initial Execution
The fundamental difference between a standard while loop and a do-while loop is the timing of condition evaluation.
whileloop- Checks the condition before executing the loop body.
- Can execute zero or more times.
- Stops when the condition becomes
false. - Pattern: Check → Execute
do-whileloop- Executes the loop body before checking the condition.
- Always executes at least once.
- Continues while the condition is
true. - Pattern: Execute → Check
do-untilloop- Executes the loop body before checking the condition.
- Always executes at least once.
- Continues until the condition becomes
true. - Pattern: Execute → Check
Comparing Initial Failures
If the condition is already false before the loop starts, a standard while loop skips execution entirely, whereas a do-while loop still runs its body once:
$ConditionState = $false
Write-Output "Testing standard while loop:"
while ($ConditionState) {
Write-Output "This line will NEVER print."
}
Write-Output "`nTesting do-while loop:"
do {
Write-Output "This line executes exactly ONCE despite the false condition."
} while ($ConditionState)where:
while ($ConditionState)evaluates$falseimmediately and skips the block.do { ... } while ($ConditionState)executes the block first, then evaluates$ConditionState, finds it$false, and terminates.
pwsh ./execution-guarantee.ps1Testing standard while loop:
Testing do-while loop:
This line executes exactly ONCE despite the false condition.Comparison Matrix
| Feature | while |
do-while |
do-until |
|---|---|---|---|
| Condition Test Timing | Top (pre-test) | Bottom (post-test) | Bottom (post-test) |
| Minimum Iteration Count | 0 |
1 |
1 |
| Exit Condition | When condition evaluates to $false |
When condition evaluates to $false |
When condition evaluates to $true |
| Best Used For | Draining queues; unknown inputs that might already be complete. | Interactive user prompts; executing tasks where initial status must be gathered before testing. | Waiting for a system transition or state change to complete. |
Practical Example: Menu-Driven Script
A common real-world use case for do-until is a menu that must display at least once before checking whether the user chose to exit.
do {
Write-Host ""
Write-Host "--- Main Menu ---" -ForegroundColor Cyan
Write-Host "1. Check Disk Space"
Write-Host "2. List Running Services"
Write-Host "3. Exit"
$choice = Read-Host "Select an option"
switch ($choice) {
"1" {
Write-Host ""
Write-Host "--- Disk Space ---" -ForegroundColor Yellow
Get-Volume |
Where-Object { $_.DriveLetter } |
Select-Object DriveLetter,
@{Name="SizeGB"; Expression={[math]::Round($_.Size / 1GB, 2)}},
@{Name="FreeGB"; Expression={[math]::Round($_.SizeRemaining / 1GB, 2)}} |
Format-Table -AutoSize
}
"2" {
Write-Host ""
Write-Host "--- Running Services ---" -ForegroundColor Yellow
Get-Service |
Where-Object { $_.Status -eq "Running" } |
Select-Object -First 5 Name, DisplayName, Status |
Format-Table -AutoSize
}
"3" {
Write-Host ""
Write-Host "Exiting..." -ForegroundColor Green
}
default {
Write-Warning "Invalid option, please try again."
}
}
} until ($choice -eq "3")where:
- The menu displays at least once, even before the user has made a choice.
- The loop repeats until the user explicitly selects option
"3"to exit.
Practical Example: Interactive Prompt and Safe Input Validation
A classic use case for do-while is interactive console validation, where you must prompt a user for input at least once, and then continue prompting them repeatedly until they supply a valid, non-empty response:
param ()
$TargetHost = ""
$AttemptCount = 0
do {
$AttemptCount++
$TargetHost = Read-Host -Prompt "Enter target server hostname (cannot be blank)"
$TargetHost = $TargetHost.Trim()
if ([string]::IsNullOrWhiteSpace($TargetHost)) {
Write-Warning "Input validation failed: Hostname cannot be empty. Please retry."
}
} while ([string]::IsNullOrWhiteSpace($TargetHost))
[PSCustomObject]@{
ConfiguredHost = $TargetHost
ValidationPass = $true
PromptsRequired = $AttemptCount
Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
}where:
do { ... }ensures the user is prompted for a hostname at least once without having to duplicate theRead-Hostline before the loop.[string]::IsNullOrWhiteSpace($TargetHost)verifies whether the input string contains valid characters.while (...)forces the loop to re-prompt the user if they entered an empty string or hit Enter without typing.
Execute the script from the console:
pwsh ./prompt-valid-input.ps1Enter target server hostname (cannot be blank):
WARNING: Input validation failed: Hostname cannot be empty. Please retry.
Enter target server hostname (cannot be blank):
WARNING: Input validation failed: Hostname cannot be empty. Please retry.
Enter target server hostname (cannot be blank): web-prod-01
ConfiguredHost : web-prod-01
ValidationPass : True
PromptsRequired : 3
Timestamp : 2026-10-24 21:15:40