Understanding Loops in PowerShell
*If you're just joining us, this post is part of a 12-part series on Getting Started with PowerShell. If you'd like to catch up (or skip ahead - more posts coming soon), click on the appropriate post below.
Getting Started with PowerShell
Windows PowerShell, PowerShell Core and PowerShell: Huh?
Installing PowerShell and Visual Studio Code
Running PowerShell Commands and Getting Help
Working with the PowerShell Pipeline
When you begin to write more advanced PowerShell scripts, there will come a time when you need to do something with collections of things. You'll need to create multiple users, find a bunch of Azure virtual machines or remove a set of files. The loop construct is your best friend in these types of tasks.
What's a Loop?
The loop is a common programming construct in nearly all programming languages. The basic premise is code that runs repeatedly based on a certain stop criterion. The stop criteria could be a certain number of items in an array, a certain number of iterations (one loop execution) or while a piece of code returns a specific value.
The loop is a construct that prevents you from having to repeat yourself in code by performing some action X number of times.
Loop Types
There are four distinct types of loops in PowerShell.
foreach
for
while
do
The ForEach Loop
The most common type of loop in PowerShell is the foreach loop. The foreach loop is a loop that runs code against a certain number of items in a collection. This loop will keep iterating a collection until it has processed all items in that collection.
For example, perhaps you have an array you've created with all text files in a particular directory using Get-ChildItem
. This command below returns each text file that exists in the C:\Files directory.
Get-ChildItem -Path 'C:\Files' -File -Filter '*.txt'
The ForEach-Object Cmdlet
You'd now like to find a certain string in each of those files. The first way to process all items in a collection is to use the ForEach-Object
cmdlet. To do that, you'll first need to read the contents of each file by running Get-Content
and using each file's path as the value for the Path
parameter.
You can see below, the code is piping each text file in the C:\Files directory to the ForEach-Object
cmdlet. That command then runs Get-Content
for each item it receives via the pipeline.
Get-ChildItem -Path 'C:\Files' -File -Filter '*.txt' | ForEach-Object {
Get-Content -Path $_.FullName
}
The foreach Statement
Another way to create a foreach loop in PowerShell is by using the foreach statement. Unlike the ForEach-Object
cmdlet, this loop is a statement. It is not a compiled cmdlet but serves a similar purpose. The big difference here is that the ForEach-Object
processes each item one at a time. The foreach statement processes all items in one shot.
Using the example above using a foreach statement, you'd first collect up all of the files with Get-ChildItem
. Then, once all of the files were collected, iterate over each one as shown below.
Notice now you're collecting up all of the files in a variable then iterating over each one. The foreach
statement also allows you to assign your own variable to the iterator ($file
) vs. $_
.
$files = Get-ChildItem -Path 'C:\Files' -File -Filter '*.txt'
foreach ($file in $files) {
Get-Content -Path $file.FullName
}
The For Loop
The for loop is great for times when you'd like to run some code a certain number of times. Perhaps you'd like to assign a few Azure virtual machines' names AZVM1, AZVM2, AZVM3. The only difference in these names is the incrementing digit. This is a great candiate for a for loop.
The example below first sets the starting iterator number at 1. The for loop will start at number 1. It then defines the condition that dictates when the loop will stop. In this case, the for loop will stop as soon as the iterator value is less than four. Finally, it is incrementing the iterator by one for each iteration.
When you run the below code, you'll see that you are returned AZVM1, AZVM2, AZVM3. If you need to add more, you'd change the condition ($i -lt 4
).
for ($i = 1; $i -lt 4; $i++) {
"AZVM$i"
}
The While Loop
If you need to run code while a certain value is true, you can use a while loop. A while loop runs a snippet of code as long as a condition is true. While loops are good for writing a script that waits on a particular condition.
Let's say you need a script to wait while a certain process is running. To create this while loop, you must first create a small snippet of code that returns a true condition. In this example, you need some code to determine if a process is running or not. You could use Get-Process -Name [process_name] -Ignore
to do that. If the process isn't running, the code will return nothing otherwise, it will return the process.
Using notepad as an example, below you can see an example of continually checking to see if the notepad process is running. While it is running, the code will tell you it's waiting and then wait a second for each iteration.
while (Get-Process -Name notepad -ErrorAction Ignore) {
Write-Host "We're waiting on notepad to stop..."
Start-Sleep -Seconds 1
}
The Do Loop
The do loop is similar to the while loop. Unlike the while loop which tests the condition at the start, the do loop tests the condition at the end of the loop. Using the example above re-purposed for a do loop, you can see below the exact same result.
When running this code snippet, you'd notice that even if notepad is not running, you'll still get a We're waiting on notepad to stop...
line returned. This happens because the do loop runs the code inside of the loop first before checking the condition.
do {
Write-Host "We're waiting on notepad to stop..."
Start-Sleep -Seconds 1
} while (Get-Process -Name notepad -ErrorAction Ignore)
Summary
Loops are commonplace in PowerShell. Use loops whenever you can to prevent code duplication. Loops are an intermediate construct that help you get things done with less code and more efficiently.