Conditional Logic Using Loops
PowerShell Tutorial 9: Getting Loopy
The last PowerShell training session introduced Conditional Logic, controlling execution of script blocks with if and switch statements. Looping is a basic programming process that allows us to repeat a command and process large amounts of data. Without this ability, writing scripts would be tedious if not next to impossible.
Here are the loops we will be working with in this tutorial:
- do while - Script block executes as long as condition value = True.
- while - Same as "do while."
- do until - Script block executes until the condition value = True.
- for - Script block executes a specified number of times.
- foreach - Executes script block for each item in a collection or array.
do while and while
do while and while Loops executes "while" the condition value stays True.
Syntaxes:
do while
do {code block}
while (condition)
while
while (condition) {code block}
Both loops run code block while the condition is True. the difference between the two; do while runs the code block (at least once) before testing the condition, while tests the condition before executing the code block. So if required, you can always count on the do while loop to execute the code block at least once, even if the condition = False. This is one of those rules you will want to remember when troubleshooting scripts.
Example: do while - Count to 5
Type the code into notepad and save with the .ps1 file extension or copy and paste into PowerShell.
$i = 1
do {Write-Host $i; $i++}
while ($i -le 5)
- or written like this -
$i = 1
do {
Write-Host $i
$i++
}
while ($i -le 5)
Output:
1
2
3
4
5
What the code is doing:
1. Creates the variable $i and assigns the Integer value of "1".
2. do - Code block writes the output of the variable. If you remember, $i++ increments the value of the variable by 1 each time the code block is executed (looped).
3. while - The condition statement compares the value of $i to the number 5, using the comparison operator -le (less than or equal to). As long as the condition is true, the loop continues to run.
This simple example is just to help you with understanding the concepts. Let's look at the same example using the while loop.
$i = 1
while ($i -le 5) {Write-Host $i; $i++}
- or written like this -
$i = 1
while ($i -le 5) {
Write-Host $i
$i++
}
In the code examples I'm showing both ways of writing script blocks, either single line or multi-line. Basically, the semi-colon (;) is a delimiter which allows us to separate commands when writing code on a single line. The <carriage return> or <enter> delimits code when using multiple lines. Either way is correct, it just depends on how long your command statements become.
Real-World PowerShell example:
Scenario: Our company is having application issues with Notepad and it is mission critical (LOL). It can actually be any application in your environment. We have been asked to monitor the application and report any failure and log the time the failure occurred.
Solution: We're going to use PowerShell to build a simple script to monitor the process and report when a failure occurs. We're going to use a do while loop to watch the "Responding" property of the "Get-Process" object cmdlet. From an earlier PowerShell training session I showed you how to get an object's properties and methods using the "Get-Member" cmdlet. As already stated, we will be using the "Responding" property for our script.
Get-Process | Get-Member<enter>
Step 1. Close all current running versions of Notepad (if applicable).
Step 2. Write the script, save as NoteMon.ps1
#launches Notepad
Notepad
#Setup a do while loop that does nothing while property value is True.
do {}
While (get-process notepad | select -Property Responding)
#Code to run when loop stops (when notepad is closed)
$strTime = get-date
Write-Host "The Application Notepad failed to respond on: $strTime"
Step 3. Run the NoteMon.ps1 script. Move notepad so that you can see the PowerShell console.
Step 4. Exit or Close Notepad. The following is reported to the console window: "The Application Notepad failed to respond on: 10/25/2007 14:16:07"
Run the script again if you wish to restart the application. OK, so kind of a simple "poor-man's" example of creating a monitoring system. Again, it's provided to help you understand the looping concept and to give you a better real world example, rather than just counting numbers.
Additional Reading:
do until
The do until loop is the opposite of do while. It executes the code block until the condition is True, in other words it runs while the condition value is False.
Syntax:
do {code block}
until (condition)
Example:
$i = 1
do {Write-Host $i; $i++}
until ($i -gt 5)
Output:
1
2
3
4
5
In this example the script block runs until the variable ($i) is incremented to the value of 6. Since 6 is greater than 5 the condition value becomes True, halting the loop from executing further.
Real-World PowerShell Example:
Jerry Lee Ford's book (Microsoft Windows PowerShell Programming for the absolute beginner) teaches PowerShell through game creation. It's a fun book that I recommend for beginners, "No Experience Required." You can read my review and pick up a copy from this site. Jerry uses the do until loop to control the collection of user input. Here is the example.
$strResponse = "Quit"
do {$strResponse = Read-Host "Are you sure you want to quit application? (Y/N)"}
until ($strResponse -eq "Y")
Image 9.0
Looking at the image, I copied the code into PowerShell and typed in numerous responses. Each time the response did not equal "Y" the value of the condition was False, therefore the script block was execute again and prompted the user for input.
for
The standard use of the for statement it to run the code block a specified number of times.
Syntax:
for (initialization; condition; repeat)
{code block}
Note: each of the three parameters are optional.
- initialization - one or more commands, separated by commas, that run before the loop begins. Commonly used to create and initialize a variable with a starting value. This variable is the basis for the condition to be tested in the next portion of the for statement.
- Condition - Resolves to a Boolean True or False value. PowerShell evaluates the condition each time the loop runs. If the condition is True the code block is executed, then the condition is tested again.
- Repeat - one or more commands, separated by commas, that run each time the loop repeats. Commonly used to modify a variable that is tested inside the "condition" portion of the for statement.
Example:
for ($i=1; $i -le 5; $i++)
{Write-Host $i}
Output:
1
2
3
4
5
Couple of things to take note:
- We delimit each parameter with a semi-colon (;). Don't confuse this with separating commnads within a parameter using commas.
- We assigned the variable ($i=1) within the condition statement. Since parameters are optional we could have assigned the variable outside of condition statement. If you omit a parameter you are required to still use (;) to delimit the parameters, example in BLUE. Here is the example showing variable assignment outside the condition and how it would look.
$i=1
for (; $i -le 5; $i++)
{Write-Host $i}
Test what would happen if you omit a parameter forget to delimit (;) and attempt to run the code.
$i=1
for ($i -le 5; $i++)
{Write-Host $i}
OK - don't panic, your in an infinite loop!!! Just type "Ctrl + C" to quit PowerShell execution. Forgetting to delimit an omitted parameter can cause some unexpected results, be careful.
If your code becomes large you can always delimit using <carriage return>.
for ($i=1
$i -le 5
$i++)
{write-host $i}
The for loop can also be used to process elements within an array.
$ints = @( 1, 2, 3, 4, 5)
for ($i=0; $i -le $ints.Length - 1; $i++)
{Write-Host $ints[$i]}
Do you know why the example sets the variable ($i) to 0 (zero) rather than 1? Remember from the "Arrays" tutorial that arrays are indexed starting with the number 0. If we set the variable to 1, our output would have been 2 3 4 5 <blank>.
The condition statement states, run script block as long as $i is less than or equal to 4. $int.Length (length would be 5 entries in the array) so 5 - 1 = 4. When the Write-Host cmdlet runs it gives us the following entries (0=1, 1=2, 2=3, 3=4, 4=5).
Output:
1
2
3
4
5
You should not have any issues working with arrays, as long as you remember that indexing begins with 0 (zero). I dealt with this in VBScript and was hoping it would have changed with PowerShell, maybe a later release?
Additional Reading:
foreach
The foreach loop description from the PowerShell Help file:
The foreach statement (also known as a foreach loop) is a language construct for stepping through (iterating) a series of values in a collection of items.
The simplest and most typical type of collection to traverse is an array. Within a foreach loop it's common to run one or more commands against each item in an array.
Note: Those writing VBScript code will have to get use to not using "Next" as PowerShell doesn't require the keyword to move to the next item in the collection.
Syntax:
foreach ($<item> in $<collection>)
{code block}
Sticking with our number example, let's see how the foreach loop works
$ints = @(1, 2, 3, 4, 5)
foreach ($i in $ints)
{Write-Host $i}
Output:
1
2
3
4
5
Looks a little like the for loop example presented earlier. However, notice that we are not testing a condition. The foreach loop runs the script block against each element within the array. It doesn't required testing a condition and it doesn't care how many elements exist in the array. You point it and it runs. As stated in the help file, foreach loop is the most typical statement used when working will arrays and collections.
I have presented many examples of foreach loops on this site. Most of the Microsoft PowerShell Scripts in the Scripting Center use this loop.
Real-World Examples:
How about listing processor information.
$strComputer = "."
$colItems = get-wmiobject -class "Win32_Processor" -namespace "root\CIMV2" `
-computername $strComputer
foreach ($objItem in $colItems) {
write-host "Caption: " $objItem.Caption
write-host "CPU Status: " $objItem.CpuStatus
write-host "Current Clock Speed: " $objItem.CurrentClockSpeed
write-host "Device ID: " $objItem.DeviceID
write-host "L2 Cache Size: " $objItem.L2CacheSize
write-host "L2 Cache Speed: " $objItem.L2CacheSpeed
write-host "Name: " $objItem.Name
write-host
}
Listing disk information
$strComputer = "."
$colItems = get-wmiobject -class "Win32_DiskDrive" -namespace "root\CIMV2" `
-computername $strComputer
foreach ($objItem in $colItems) {
write-host "Description: " $objItem.Description
write-host "Device ID: " $objItem.DeviceID
write-host "Interface Type: " $objItem.InterfaceType
write-host "Media Type: " $objItem.MediaType
write-host "Model: " $objItem.Model
write-host "Partitions: " $objItem.Partitions
write-host "Size: " $objItem.Size
write-host "Status: " $objItem.Status
write-host
}
Let's say we want to determine which processes are running on our computer.
foreach ($item in Get-Process)
{if ($item.Responding -eq "True"){Write-Host $Item.Name}}
Just showing that we can add the conditional logic if statement within the foreach loop to give us more control over the data we wish to retrieve.
Notice the code block - {{}} - what I've done is called nesting. Perfectly legal and a concept that we will discuss as you progress through these PowerShell Training sessions.
Additional Reading:
Varying Loop Processing
There are two statements, break and continue, that allow you to alter the execution of a loop when certain conditions are met.
The Break Statement
Excerpt: Taken from the PowerShell help file.
A break statement appearing in a code block of a foreach, for, while, or do loop or in a switch statement, ends the code block. In the case of the looping statements, the break statement causes the loop to exit. In the case of the switch statement, the break statement causes a code block inside of a switch statement to exit and thus the entire switch statement exits.
The following example shows how to use a break statement to exit a for statement:
for($i=1; $i -le 10; $i++)
{
Write-Host $i
break
}
In this case, the break statement exits the for loop when $i equals 1. Even though the for statement evaluates to true until $i is greater than 10, the Windows PowerShell reaches the break statement the first time through the for loop.
It's more common to see the break statement used inside of a loop where there is some inner condition to be met. Consider the following foreach statement example:
$i=0
$varB = (10,20,30,40)
foreach ($var in $varB)
{
$i++
if ($var -eq 30)
{
break
}
}
Write-Host "30 was found in array position $i"
In this example, the foreach statement iterates the $varB array. Each time through the code block, the $i variable is incremented by 1. The if statement inside evaluates to false the first two times through the loop. The third time through the loop, $i equals 3 and the $val variable equals 30, at which point the break statement runs and the foreach loop exits.
Additional Reading:
The Continue Statement
Excerpt from PowerShell help file:
In a script, the continue statement causes program flow to move immediately to the top of the innermost loop controlled by any of these statements:
· for
· foreach
· while
EXAMPLE
In this example, program flow returns to the top of the while loop if $ctr is equal to 5. The result is that all numbers between 1 and 10 except 5 are displayed.
while ($ctr -lt 10)
{
$ctr += 1
if ($ctr -eq 5){continue}
Write-Host $ctr
}
Note that in a for loop, execution continues at the first line in the loop. If the arguments of the for statement test a value that is modified by the for statement, an infinite loop can result.
In this PowerShell Training session you learned how to control script block flow using loop processing. You were also able to process large amounts of data by looping through a collection and enumerating properties of an object. In up-coming PowerShell tutrials, we will be looking a Functions and more Script Block detail. See you then…
Email This Page



Alan Starkey:
I have worked my way through the existing tutorials and found them extremely useful. Eagerly awaiting the next instalment.
6 November 2007, 8:22 amRyan:
hay ,
just wondering if you can tell me how to use the same examples you have for waiting for a process to start .. i can’t seem to find any examples to try learn
21 November 2007, 9:15 amJesse Hamrick:
Hi Ryan,
So it sounds like you want to do the opposite of what was discussed in the tutorial. You want to be notified when a process starts not stops. Easy enough…
Think about the loop type we would need to do this. In the tutorial example we use “do while” meaning run the script block while the condition is “True.” What we want to do, regarding your question, is run the loop “until” notepad starts; meaning run the code-block until the condition becomes “True.” For this we will use a “do until” loop.
Until (Get-Process notepad | select -Property Responding)
$strTime = Get-Date
Write-Host “The Application Notepad started on: $strTime”
The code above works but there is a slight problem. Since we are running the Get-Process cmdlet and notepad is not running, we get a bunch of errors in the output. To keep it clean we need to suppress the errors in the output:
Until (Get-Process notepad -ErrorAction SilentlyContinue | Select -Property Responding)
$strTime = Get-Date
Write-Host “The Application Notepad started on” $strTime
Copy this code into a .ps1 file and run the script. While the script is running launch notepad. The console output should look similar to:
The Application Notepad started on: 11/21/2007 11:51:46
Hope that helped,
21 November 2007, 1:00 pmJesse Hamrick
Arlo Belshee:
A quick explanation of why arrays start with 0: to decrease errors when iterating.
Basically, instead of using -le when iterating, use -lt. Then the script becomes for($i=0; $i -t ary.Length; $i++){}. You no longer have to subtrace 1 from the length. This helps reduce off by one errors.
More generally, this is an example of the half-open range concept. A half-open range with bounds 1 and 3 would be written [1, 3), and include the number {1, 2}. A closed range would include both bounds: [1, 3] means {1, 2, 3}.
The reason you care is that for non-integer data, such as decimals or dates, it is usually easier to define a range as half-open than it is as closed. For example, how do you define the time range “before noon today”? With a half-open range, you say [’5/17/2008′, ‘5/18/2008 12:00′). With a closed range, you need to know the exact last possible momemt before noon, so that you can use it as the end of your range.
Because arrays start at 0, iterating them or selecting sub-ranges from within them uses exactly the same script as selecting sub-ranges of dates. You can just always use -lt - which means you don’t have to do different things in different places, and are less likely to make mistakes.
17 May 2008, 11:39 amEric:
The do/while approach seems to be a bad idea - powershell will peg the CPU sitting in the tight monitoring loop.
18 May 2008, 11:59 amAm I missing something?
Roshan:
What is the keyword to move to the the next Item in a foreach ?
29 May 2008, 11:21 amJesse Hamrick:
PowerShell doesn’t require a “Next”
30 May 2008, 2:44 pmNote: Those writing VBScript code will have to get use to not using “Next” as PowerShell doesn’t require the keyword to move to the next item in the collection.
FMoses:
Eric,
Within the do-until loop add the following statement…essentially it puts your script to sleep for 60sec and then checks the process state every time it wakes up. Check “get-help sleep” for more info.
{ sleep 60 }
cheers
18 June 2008, 2:35 pmNorman:
Jerry Lee Ford’s example of getting user input is, perhaps deliberately, pointless. The user is locked into a mode where the only valid input is “Y” whereupon the program terminates. The only choice the user has is to delay the inevitable. Asking “Are you sure you want to quit the application?” is kind of cheeky!
14 July 2008, 6:25 amWill:
Running through the second Break Statement example I get $i = 4, thus the Write-Host “30 was found in array position 4″. You wrote it should be $i = 3. Now I won’t say I’m not missing something…but what am I missing? Thanks.
23 July 2008, 11:54 amJesse Hamrick:
You’re not missing anything, there was a typo in the example:
Line: if ($val -eq 30)
Should be: if ($var -eq 30)
Then you would get “30 was found in array position 3″
I’ve made the correction in the tutorial.
23 July 2008, 1:22 pm