Loops are an essential part of programming, and PowerShell is no exception. They allow you to iterate through collections and repeat tasks until a condition is met. Loops can drastically enhance your PowerShell scripts and take your automation to the next level.
Join me as I break down the different types of loops in PowerShell. If it helps, pour yourself an unhealthy-sized bowl of Froot Loops, and we’ll get started. And if you don’t like Froot Loops, just know that I’m silently judging you from across the internet.
What are the different types of loops in PowerShell?
PowerShell utilizes many of the same loop mechanisms you’ll find in other programming languages, with some variations unique to PowerShell. Here are the different types of loops in PowerShell:
For
loopForeach
loopWhile
loopDo while
loopDo until
loop
Let’s review them to see how they function, what makes them unique, and when you should use them.
PowerShell For loop
The for
loop is very common in programming and functions much the same way in PowerShell as it does in other programming languages. For
loops run as long as the specified condition evaluates to $true
. When the condition evaluates to $false
, the loop terminates. The for
loop is excellent for iterating through a statement a fixed number of times.
Here is the basic structure of a for
loop:
for (<Init>; <Condition>; <Repeat>) {
<Statement list>
}
Parentheses contain the for
loop statement, and semicolons separate each component of the statement. The first component, represented by <Init>, represents one or more commands that run before the loop begins. Typically, <Init> is used to initialize a variable with a starting value that is utilized in the test condition.
The <Condition> placeholder represents a conditional expression that resolves to a $true
or $false
Boolean value and typically evaluates the initialized value at the beginning of the loop. The loop and its command block continue to run as long as the value of the condition returns $true
. The loop terminates when the condition returns $false
.
The <Repeat> placeholder represents one or more commands that execute each time the loop repeats. Typically, this portion of the statement is used to increment the value of the variable used in the conditional expression.
<Statement list> represents the set of commands that repeat as the loop iterates.
Here's an example of a for
loop in its most basic form:
for ($i = 1; $i -lt 10; $i++){
Write-Host $i
}
In this example, we’re initializing the variable $i
with the value of 1. Next, we’re using the less than (-lt
) comparison operator to check if the value of $i
is less than 10. If the comparison returns $true
, the commands in the statement list execute. In this case, we run the command Write-Host $i
. Next, we increase the value of $i
by one with the expression $i++
. This process repeats until the value of $i
is 10, at which point the condition $i -lt 10
returns false, and the loop terminates.
Now, let’s look at a more practical example. Let’s say I have a CSV file with six entries. However, due to a processing error, each entry has a duplicate entry resulting in a CSV file with 12 total entries. I can use a for
loop to iterate through the CSV file and return only every other entry to weed out the duplicates. Here's my CSV file:
And here is the PowerShell script to return every other line of the CSV file:
$users = Import-Csv -Path "C:\CSV\the_office_users.csv"
for ($i = 0; $i -lt $users.count; $i+=2){
$users[$i] | Export-Csv -Path "C:\CSV\updated_office_users.csv" -Append
}
Here is the resulting CSV file after running the script:
This example is a little trickier than the last one, so bear with me. In this example, we’re importing all the data from the CSV file and assigning it to the $users
array. In the for
loop, we’ve set $i
to a value of 0 to correspond to the index value of the $user
array. For the comparison, we’re comparing $i
to $users.count
, which goes to 11 since the CSV file contains 12 entries and the array starts at 0. The command statement $users[$i]
then executes, which grabs the user data assigned to the current array index, then exports that information to a new CSV file. We add the -Append
parameter to the Export-Csv
command to ensure each value is added to the file as it loops through the statement. The value of the initialization variable then increases by two using the expression $i+=2
. Increasing the initialization variable by two for each loop lets us return every other row of data.
Now, I know what you’re thinking: This sure seems like a lot of work for just a CSV file with 12 rows of data. You’re not wrong. But if this CSV file had 1,200 rows of data, this would be a lifesaver.
PowerShell foreach loops
The
foreach
loop in PowerShell is used to iterate through a collection of items and is gloriously powerful. But it can also be a bit... weird. When we’re talking about the foreach
loop, we could be talking about one of four things:
foreach
foreach
ForEach-Object
Foreach
Confused yet? Glad we’re on the same page. Let’s try to make sense of this mess Microsoft has got us in.
There are (kind of) four versions of the foreach
loop in PowerShell. There’s the foreach
statement, the foreach
method, the ForEach-Object
cmdlet, and the Foreach
alias. Clear as mud? Perfect. Let’s quickly go over each one.
Foreach: The statement
Foreach
, the statement, is my personal favorite of the foreach
loop varieties. It’s also the one people use the vast majority of the time. Here’s the basic syntax of the foreach
statement:
foreach ($<item> in $<collection>){
<statement list>
}
Let’s dive right into a simple example to examine how the foreach
statement works.
$darths = “Plageuis”, “Bane”, “Sidious”, “Nihilus”, “Revan”, “Vader”
foreach ($darth in $darths){
Write-Host “Darth $darth” -ForegroundColor Red
}
In this example, we assign several Sith Lord names to the $darths
variable. Then, we start the foreach
statement and assign the $darth
(no “s”) variable as a placeholder to contain the current item being iterated on from the $darths
collection. Lastly, we’re using Write-Host
(because we don’t care what the internet thinks of us) and displaying the list of darths in red, which seems suitable. Here’s the result.
While there are instances when the other foreach
loops would be more efficient, the foreach
statement will likely be your go-to foreach
loop most of the time because of readability and performance.
Foreach: The method
The foreach
method is the newest of the foreach
loops added to PowerShell. Being a method means it’s quick and easy to call this type of foreach
loop, and it can often be a single line of code. The foreach
method can also be considerably faster depending on the use case but can suffer with its readability. Here’s an example of the foreach
method:
(Get-Process).ForEach({$_.Id})
In this example, we’re using the foreach
method to iterate through each returned object from the Get-Process
cmdlet, then return the Id of each item. Here's a truncated version of the returned results.
As I mentioned, the foreach
method can be considerably faster depending on the use case. For example, invoking a method on each loop iteration can be noticeably faster using the foreach
method. For example:
(Get-Process -Name notepad).ForEach(‘Kill’)
In this example, I’m invoking the ‘Kill’
method on each returned process with the name notepad
. This command processes much faster than using a foreach
statement. However, the best way to determine the efficiency of a command is to use the Measure-Command
cmdlet, which can tell you exactly how long it takes for a command to run.
ForEach-Object: The cmdlet
The ForEach-Object
cmdlet is a great way to integrate a foreach
loop into a pipeline. The ForEach-Object
cmdlet also offers advanced functionality, such as supporting the begin
, process
, and end
function blocks. However, because ForEach-Object
accepts input from the pipeline, it can be considerably slower than the other options we’ve covered.
Here’s an example of a command using the ForEach-Object
cmdlet.
$Grades = 23,22,21,20,19,18,17,16,15,14,13,12
$Grades | ForEach-Object {[Math]::Round($_/23*100)}
In this example, we want to determine the possible scores for an assignment with 23 questions depending on the number of correct answers. This script provides the score for anything above 50%. We’ve assigned the possible correct answers to the $Grades
variable and piped that to the ForEach-Object
cmdlet. Then, we utilized the Math
class’s Round
method, which rounds the results of our equation ($_/23*100)
to the nearest whole number. Here's the result:
Foreach: The alias
Last and definitely least is the Foreach
alias. The Foreach
alias is the alias for the ForEach-Object
cmdlet. I’m pretty sure its sole reason to exist is to confuse people about which foreach
loop is being used.
When should you use the Foreach
alias? Never. Okay, fine. If you want to use the alias in your one-off command because you’re too lazy to type “-Object” and can’t be bothered with tab completion, then fine. But please don’t ever use this in a script that other people may read one day. You’re just going to cause confusion, and the world is a confusing enough place as it is.
Whew, that felt good to get off my chest. Now that I’ve ranted, let’s move on to while
loops.
PowerShell while loops
While
loops in PowerShell are the perfect combination of simplicity and power. They’re readable, easy to understand, and can be used to great effect.
While
loops run while a condition evaluates to true. They’re similar to for
loops, but their syntax is much less complicated. Here is the basic structure of a while
loop:
while(<condition>){
<statement list>
}
When the while
statement runs, PowerShell evaluates the condition before running anything in the <statement list> block, which means the loop never runs if the condition never evaluates to true. As long as the condition evaluates to true, the loop continues to run until the condition returns false or the loop is stopped by another means, such as a break
statement.
Here’s an example of a while
loop:
$Computer = "thor.whiskeytime.club"
$Proc = Invoke-Command -ComputerName $Computer {Get-Process -Name notepad -ErrorAction SilentlyContinue}
while($Proc){
$Proc = Invoke-Command -ComputerName $Computer {Get-Process -Name notepad -ErrorAction SilentlyContinue}
Write-Host “Notepad is currently running on $Computer”
Start-Sleep -Seconds 10
}
Write-Host “Notepad is not running on $Computer”
In this example, we’re checking to see if the Notepad process is running on a remote computer. If it is, we enter the while
loop, which checks to see if the process is still running. It then writes the status of the remote process to the console. The script then sleeps for 10 seconds. After 10 seconds, the loop reevaluates the condition, repeating until the condition is false, meaning the process is no longer running on the remote computer. Once the loop exits, we indicate that the Notepad process is no longer running on the remote computer.
PowerShell do while loop
The do while
loop in PowerShell is similar to the while
loop but is a bottom-evaluated version. A bottom-evaluated loop just means that the condition is not evaluated until after the statement block has run at least once.
Here’s the syntax of a do while
loop:
do {
<statement list>
} while(<condition>)
And here’s a simple example of a do while
loop where the user has to guess the correct number between 1 and 50. If they guess the correct answer, the loop terminates.
$num = 42
do{
$guess = Read-Host -Prompt “Guess a number between 1 and 50:”
} while($guess -ne $num)
Write-Host “Congratulations, you know the meaning of life.”
And here's the result:
PowerShell do until loop
Do until
loops are basically the opposite of do while
loops. Do until
loops iterate until a condition is true. A do until
loop iterates as long as the condition evaluates to false. If we were to use the previous example and switch it for a do until
loop, the first time a user guessed the wrong answer, the loop would terminate.
PowerShell has a loop for every occasion
I don’t know about you, but that was enough loops to get my head spinning.
PowerShell loops are handy, especially when dealing with repetitive tasks and extensive collections. If you want to get really advanced, you can play around with embedded loops and combine them with if statements.
Once you get the hang of PowerShell loops, you can use that knowledge to build your own custom PowerShell scanners in PDQ Inventory. With PowerShell scanners, you can query your managed computers and return loads of valuable information. Try it out for yourself with a 14-day free trial.
Oh, and if you want to spruce up your Visual Studio code like the screenshots in this article, we’ve got you covered with a complete guide on customizing Visual Studio code.