Taking Control with PowerShell Logic
The Learn PowerShell series has shown that even basic PowerShell can solve complex problems. As your PowerShell capabilities increase though, you’ll likely discover the need to take more control of your PowerShell logic. Like other computer languages, you will need to ability to add controls such as looping and/or logic branches.
Video
If you prefer video format over written documentation I discuss this topic in the following TechThoughts video:
Controlling PowerShell Logic Flow
PowerShell is no different than other computer languages in that more complex capabilities require the addition of logic. If you are familiar with logic controls in another programming language, you’ll find no surprises. We’ll simply cover some of the specific PowerShell syntax.
If programming logic is a relatively new concept I’d like to encourage you not to shy away from this component of PowerShell. The addition of PowerShell logic is simple, and relatively straightforward. Take a look at this quick code example to see how easy these PowerShell logic controls are to implement:
# do something based on a condition
if ('a condition is met') {
# do something / take an action
}
elseif ('a different condition is met') {
# do something different
}
else {
# no conditions met, do something else
}
Conditional
PowerShell utilizes if, elseif, and else for conditional statements. Conditional statements perform conditional tests based on criteria you specify. The results of those tests will result in various actions that you create.
Many times in PowerShell you will need to confirm the presence of a directory or file path. Lets leverage the Test-Path cmdlet combined with some PowerShell logic to take various actions based on the discovered results.
$path = 'C:\Windows' #windows
# $path = /home #linux
$evalPath = Test-Path $path
if ($evalPath -eq $true) {
Write-Host "$path VERIFIED"
}
elseif ($evalPath -eq $false) {
Write-Host "$path NOT VERIFIED"
}
In this basic example we load up the variable $path with a directory location. We then evaluate that path with Test-Path and store the results in $evalPath. Test-Path returns only a boolean value ($true/$false) so we use conditional logic to determine if that variable contains a true or false value. Based on the results, we take a different action.
Because Test-Path only returns two possible values the above example can also be written with an else to achieve the same results:
$path = 'C:\Test' #windows
# $path = /home #linux
$evalPath = Test-Path $path
if ($evalPath -eq $true) {
Write-Host "$path VERIFIED"
}
else {
Write-Host "$path NOT VERIFIED"
}
Comparison Operators
Notice that conditional evaluations make heavy use of comparison operators. Comparison operators allow you to specify certain conditions. Here are a few common PowerShell comparison operators:
-eq equals
-ne not equals
-gt greater than
-ge greater than or equal to
-lt less than
-le less than or equal to
-like wild card pattern match
-notlike wild card pattern match
Visit the PowerShell documentation on comparison operators for a full list of all available operators.
Switch
A switch statement is much like using an if. The difference is that it is capable of evaluating multiple conditions. You could achieve the same thing utilizing multiple if statements. Switch statements are just a bit cleaner to look at, and simpler to write. Try running the following example on your computer. Then add additional switch statements to account for numbers up to 10.
[int]$aValue = Read-Host 'Enter a number'
switch ($aValue) {
1 {
Write-Host 'You entered the number ONE'
}
2 {
Write-Host 'You entered the number TWO'
}
3 {
Write-Host 'You entered the number THREE'
}
4 {
Write-Host 'You entered the number FOUR'
}
5 {
Write-Host 'You entered the number FIVE'
}
Default {
Write-Host "Sorry, I don't know what to do with $aValue"
}
}
Loops
Loops are one of the base logical structures of programming. They enable your code to perform repetitive tasks quickly, or evaluate a lot of data rapidly. If you have implemented loops in another language, you’ll find no surprises in PowerShell.
If you aren’t familiar with implementing loops, I provide several practical examples below. Again, don’t shy away from these logical control mechanisms. They aren’t “just for developers”. These PowerShell logic controls are easy to implement and will take your PowerShell capabilities to the next level!
For Loop
A basic For Loop can be used to perform an action a specific number of times. Try out the rainbow example on your computer:
# rainbow example
for ($i = 0; $i -le 15; $i++) {
Write-Host $i -ForegroundColor $i
}
The rainbow example started $i at 0. Each loop $i was incremented until it reached 15. You can also go the opposite direction as in this example:
$aString = 'Jean-Luc Picard'
$reverseString = ''
for ($i = $aString.Length; $i -ge 0; $i--) {
$reverseString += $aString[$i]
}
$reverseString
Lets break that example down. Variable $aString is created and a string loaded into it. We also declare an empty $reverseString which will store the reversed string. We then start looping based on the total length of $aString (in this example 15). In each of the loops we take the one character in that loop $aString[$i] and load it into $reverseString. Because we start at the end of $aString, the first letter that gets loaded into $reverseString is the last letter. The loop continues until each character is loaded in reverse order.
Foreach Loop
In my experience, Foreach Loops tend to be used more than For loops in PowerShell. Foreach enables you to loop through all the items in a collection. A collection could be many things. It could be a collection of strings, a collection of numbers, or even a collection of complex objects. Lets dive into an example.
$path = 'C:\Test'
[int]$totalSize = 0
$fileInfo = Get-ChildItem $path -Recurse
foreach ($file in $fileInfo) {
$totalSize += $file.Length
}
Write-Host "The total size of file in $path is $($totalSize /1MB) MB."
Get-ChildItem is recursively getting all files in the specified path. Using foreach we can loop through each of those objects in the $fileInfo collection. In each iteration of the loop $file.Length (size) is evaluated. $file represents the current item in the collection that is being handled. So, on the first loop, $file represents the first item in $fileInfo. The loop will continue until every item in the collections has gone through.
While Loop
There are two types of while loops that you will encounter, a do while, and a standard while loop. The primary difference between this is that a do while will always perform the loop at least once. A while loop may or may not enter the loop depending on the criteria set. The following examples illustrate the difference:
#-------------------------------------------------
# do while loop
$pathVerified = $false
do {
# in a do while, the user will always be prompted at least once
$path = Read-Host 'Please enter a file path to evaluate'
if (Test-Path $path) {
$pathVerified = $true
}
} while ($pathVerified -eq $false)
# the loop will continue until the path is verified
#-------------------------------------------------
# while loop
$pathVerified = $true
while ($pathVerified -eq $false) {
# in a while loop, you might never enter the loop
$path = Read-Host 'Please enter a file path to evaluate'
if (Test-Path $path) {
$pathVerified = $true
}
}
# this loop was never entered because $pathVerified is $true
#-------------------------------------------------
Keep in mind that while loops will continue until the condition is met. Try running the do while loop example above. Provide paths that both exist and don’t exist on your compu
ter.
Where-Object
Where-Object is one of the most powerful cmdlets at your disposal. It behaves much like an if statement in that it uses conditional statements to evaluate criteria you specify. It also behaves like a loop in that it will evaluate every object in your pipeline. You could accomplish the same results using both if statements and loops. The benefits of Where-Object is that it allows you stay within the pipeline, and is much simpler to implement.
# Get processes using more than 50MB of memory
$largeProcesses = Get-Process | Where-Object { $_.WorkingSet64 -gt 50MB }
# do the same thing using if statements and loop
$largeProcesses = @()
$processes = Get-Process
foreach ($process in $processes) {
if ($process.WorkingSet64 -gt 50MB) {
$largeProcesses += $process
}
}
As you can see Where-Object greatly simplifies evaluating each object returned by Get-Process. Where-Object will often be your primarily filtering tool for selecting specific objects returned by various cmdlets.
ForEach-Object
ForEach-Object is the same as a Foreach loop except that you can use it inside the pipeline.
$path = 'C:\Test'
$folderCount = 0
Get-ChildItem $path | ForEach-Object -Process { if ($_.PSIsContainer) { $folderCount++ } }
$folderCount
Each file object found by Get-ChildItem is being evaluated in the ForEach-Object to determine if it is a directory (PSIsContainer) or a file. If it is a directory, $folderCount is incremented.
Putting it all together with an example
With all that covered, we can now combine various elements together to produce powerful and insightful results.
# declare a few variables for counting
[int]$fileCount = 0
[int]$folderCount = 0
[int]$totalSize = 0
# declare our path we want to evaluate
$path = 'C:\Test'
# get the file information
$rawFileInfo = Get-ChildItem $path -Recurse
# loop through that file information
foreach ($item in $rawFileInfo) {
if ($item.PSIsContainer) {
# this is a folder/directory
$folderCount++
}
else {
# this is a file, because it is not a PSIsContainer
$fileCount++
$totalSize += $item.Length
}
}
# generate output
Write-Host "Breakdown of $path"
Write-Host "Total Directories: $folderCount"
Write-Host "Total Files: $fileCount"
Write-Host "Total Size of files: $($totalSize / 1MB) MB"
Using basic cmdlets, a foreach loop, and a few conditional statements, we can provide rapid insight into a given file path!
Try running this example on your computer!
Now try removing the -Recurse switch. How does this change the output? Why?
With everything covered so far you may have recognized other ways to code this example. A switch statement could have replaced the if/else. We could have also filtered using ForEach-Object. There are many ways to solve a problem now that you can leverage PowerShell logic!
- PS1 – Should you learn PowerShell?
- Learn and use PowerShell with just three commands
- Working with the PowerShell Pipeline
- PowerShell History and Current State
- Getting setup for PowerShell Development
- Working With PowerShell Variables
- Taking Control with PowerShell Logic
- PowerShell Input & Output
- PowerShell Errors and Exceptions Handling
- PowerShell Remoting
- PowerShell Scripts
- PowerShell Functions
- Manage Cloud with PowerShell
- PowerShell Modules