PowerShell's for loop conforms to the standard established by ANSI C, that is:
for ( <INITIALISATION>; <CONDITION>; <MODIFIER> ) {
<STATEMENTS>
}
...where:
- <INITIALISATION> is where the loop iteration value is initialised;
- <CONDITION> defines the condtion that allow the loop to continue;
- <MODIFIER> is where the loop iteration value is modified (typically incremented or decremented); and
- <STATEMENTS> is the PowerShell code to be executed for each loop iteration.
Some examples:
for ( $i = 0; $i -le 10; $i++ ) {
Write-Host -Object ( '$i is {0}' -f $i );
}
for ( $i = 0; $i -le 10; $i+=2 ) {
Write-Host -Object ( '$i is {0}' -f $i );
}
for ( $i = 5; $i -ge -5; $i-- ) {
Write-Host -Object ( '$i is {0}' -f $i );
}
for ( $fahr = 0; $fahr -le 300; $fahr += 20 ) {
Write-Host -Object ( '{0,3}°F {1,8:F1}°C' -f $fahr, ((5/9)*($fahr-32)) );
}
for ( ; ; ) {
Write-Host -Object 'Hello World!';
}
for ( ; $intScore -le 21; $intScore++ ) {
Write-Host -Object ( 'The score is {0}.' -f $intScore );
}
You can exit a for loop using a statement (see
section ). For example, the loop
below will never reach values beyond 5:
for ( $i = 0; $i -le 10; $i++ ) {
$i;
if ($i -eq 5) { break; }
}
This is obviously a contrived example, as you would never use the loop iteration value to exit a for loop (that is,
you would set the loop condition properly instead). Typically, a break statement will be used to react to
an external condition, for example, human input or a calculation based on the loop iteration value and another
external value, Etc.
You can also cause the for loop to jump to the next loop iteration using a statement.
For example, the loop below will skip values 3 and 7:
for ( $i = 0; $i -le 10; $i++ ) {
if ( ($i -eq 3) -or ($i -eq 7) ) { continue; }
$i;
}
PowerShell's foreach loop provides a way to step through an array (or "collection") of objects in
a controlled manner and is, in some respects, the antithesis of PowerShell's
, in that it breaks
away from the "flow" of the pipeline, reverting to object manipulation techniques used in other
3GLs,
such as C#, PHP, Python and Visual Basic .NET. However, this divergence is well justified when
performing state affecting changes (see ).
The format of the foreach loop is as follows:
foreach ( <ITEM> in <COLLECTION> ) {
<STATEMENTS>
}
...where:
- <ITEM> is the variable that will be used to hold each successive array value;
- <COLLECTION> is the array being stepped through; and
- <STATEMENTS> is the PowerShell code to be executed for each loop iteration.
The item data type will match that of the objects being stored in the collection, for example, if we
wished to process each rule defined in the Windows firewall, we could quickly identify the data type of a firewall rule:
PS C:\Users\JohnDoe> (Get-NetFirewallRule)[0].GetType().FullName;
Microsoft.Management.Infrastructure.CimInstance
PS C:\Users\JohnDoe>
We could then define the following:
[Microsoft.Management.Infrastructure.CimInstance[]] $Local:arrFirewallRules = @();
[Microsoft.Management.Infrastructure.CimInstance] $Local:objFirewallRule = $null;
Finally, we can retrieve the firewall rules and process each one in turn:
$arrFirewallRules = Get-NetFirewallRule;
foreach ( $objFirewallRule in $arrFirewallRules ) {
$objFirewallRule.Name;
} #foreach
The foreach loop can also be used to step through more simple arrays, for example:
foreach ( $s in @('fly', 'spider', 'bird', 'cat', 'dog', 'horse') ) { $s; }
foreach ( $i in @(10..20; 30..40) ) { '192.168.0.{0}' -f $i; }
that exists only when a ForEach loop is executing. This variable is an instance of the IEnumerator .NET class and can be used in the following ways:$ForEach.Reset();
$ForEach.Current;
$ForEach.MoveNext();
I\'d argue that the only practicable use of this variable is its reset() method. Comments welcome!' );
PowerShell's while loop continues to execute the loop statements while the given condition holds true. The format of the while
loop is as follows:
while ( <CONDITION> ) {
<STATEMENTS>
}
Note that the condition is evaluated at the beginning of each loop operation; the condition must therefore hold true in order for the
loop to be entered in the first place (this differs from PowerShell's
loop, which will be discussed below).
We must therefore perform some kind of initialisation prior to entering the loop, for example:
[Int32] $Local:intSelection = 0;
while ( ($intSelection -lt 1) -or ($intSelection -gt 5) ) {
try {
$intSelection = Read-Host -Prompt 'Enter a number between 1 and 5';
if ( ($intSelection -lt 1) -or ($intSelection -gt 5) ) {
Write-Host -Object 'ERROR : Number out of range.';
}
}
catch [System.Management.Automation.MetadataException] {
Write-Host -Object 'ERROR : Please enter a number.';
}
}
PowerShell's do...while loop is similar to the loop, with the
exception that the condition is tested at the end of the loop iteration:
do {
<STATEMENTS>
} until ( <CONDITION> )
This behaviour is useful when you always want the loop operation
to execute at lease once. For example:
[ValidateSet('Y', 'N')] [String] $Local:strRetry = 'N';
[String] $Local:strFile = 'd:\test.txt';
do {
Remove-Item -LiteralPath $strFile -ErrorAction silentlyContinue;
if ( ! $? ) {
while ( $true ) {
try {
$strRetry = Read-Host -Prompt 'Unable to delete file; retry (Y/N)?';
break;
}
catch [System.Management.Automation.ValidationMetadataException] {
Write-Host -Object 'Invalid input.';
}
}
}
} while ( $strRetry -eq 'Y' );
PowerShell's do...until loop operates in the same way as the
loop, with the
exception that the loop terminating condition is reversed, that is, the loop exits when
the condition evaluates to .
do {
Write-Host -Object 'Waiting for host to come online...';
Start-Sleep -Seconds 1;
} until ( Test-Connection -ComputerName '192.168.0.1' -ErrorAction silentlyContinue )
PowerShell's break command causes processing of the immediate loop to be aborted, with execution
passing directly to the command following the loop construct. The break command is useful for dealing
with external factors, such as errors or user interaction, for example:
[String] $Local:strIP = '';
[Int32] $Local:intOctet = 0;
foreach ( $intOctet in 1..254 ) {
$strIP = '192.168.0.{0}' -f $intOctet;
Write-Host -Object ( 'Checking for HTTP listener on {0}...' -f $strIP );
Test-NetConnection -CommonTCPPort HTTP -ComputerName $strIP;
if ( (Read-Host -Prompt 'Continue (Y/N)') -ne 'Y' ) { break; }
}
As already mentioned, the break command aborts the immediate loop. However,
it is possible to break out of a parent loop (see
, below).
PowerShell's continue command causes execution of the immediate loop iteration to be halted with execution
continuing with the next iteration of the loop.
[Microsoft.Management.Infrastructure.CimInstance[]] $Local:arrTCPConnections = Get-NetTCPConnection | Where State -eq 'Established';
[Microsoft.Management.Infrastructure.CimInstance] $Local:objConnection = $null;
foreach ( $objConnection in $arrTCPConnections ) {
if ( $objConnection.OwningProcess -eq 4 ) {
Write-Host -Object 'Ignoring system process...';
continue;
}
Write-Host -Object ( 'Local address/port {0}:{1}; Remote address/port {2}:{3}' -f $objConnection.LocalAddress, $objConnection.LocalPort, $objConnection.RemoteAddress, $objConnection.RemotePort );
}
As with the command, the continue command
can also continue to the next iteration of a parent loop (again, see
, below).
Both the and
commands support an advanced mode
of operation, whereby the developer can specify which loop will be aborted or continued. This is very useful when
dealing with nested loops. Without this capability, the developer would have to maintain a flag of some kind that
would be set when aborting an inner loop; the flag would then be used as a condition to abort the outer loop.
The example below attempts to demonstrate this. In this example, we have a list of fruit that gets eaten on
various days of the week. The rules of this day/fruit relationship are as follows:
- Our fruit week starts on a Sunday;
- Fruit is only eaten on weekdays;
- We never eat cherries;
- Eating bananas on a Wednesday causes us to fall asleep; and
- Eating damsons on a Friday causes death (!).
Clear-Host;
[String[]] $arrDays = @( 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' );
[String] $strDay = '';
[String[]] $arrFruit = @( 'apple', 'banana', 'cherry', 'damson', 'elderberry', 'fig' );
[String] $strFruit = '';
:DAY_LOOP foreach ( $strDay in $arrDays ) {
Write-Host -Object ( 'Today is {0}.' -f $strDay ) -BackgroundColor Red -ForegroundColor White;
if ( ($strDay -eq 'Saturday') -or ($strDay -eq 'Sunday') ) {
Write-Host -Object ( "`t{0} is a non-fruit day!" -f $strDay );
continue;
}
Write-Host -Object "`tLet's eat some fruit..." -BackgroundColor Yellow -ForegroundColor Black;
foreach ( $strFruit in $arrFruit ) {
if ( $strFruit -eq 'cherry' ) {
Write-Host -Object "`t`tYuk, don't like cherries!" -BackgroundColor DarkGray -ForegroundColor White;
continue;
}
if ( ($strDay -eq 'Wednesday') -and ($strFruit -eq 'banana') ) {
Write-Host -Object "`t`tFeeling sleepy... snor!" -BackgroundColor DarkYellow -ForegroundColor Black;
continue DAY_LOOP;
}
if ( ($strDay -eq 'Friday') -and ($strFruit -eq 'damson') ) {
Write-Host -Object "`t`tDamsons on a Friday .... no!!!!! Errrrrrk! " -BackgroundColor Black -ForegroundColor White;
break DAY_LOOP;
}
Write-Host -Object ( "`t`t{0} - yum!" -f $strFruit );
}
}