As you begin to write scripts or commands that interact with unknown data, the concepts of looping and flow control become increasingly important.
PowerShell’s looping statements and commands let you perform an operation (or set of operations) without having to repeat the commands themselves. This includes, for example, doing something a specified number of times, processing each item in a collection, or working until a certain condition comes to pass.
PowerShell’s flow control and comparison statements let you adapt your script or command to unknown data. They let you execute commands based on the value of that data, skip commands based on the value of that data, and more.
Together, looping and flow control statements add significant versatility to your PowerShell toolbox.
Use PowerShell’s logical operators to compare pieces of data and make decisions based on them.
For a detailed description (and examples) of these operators, see Comparison Operators.
PowerShell’s logical and comparison operators
let you compare pieces of data or test data for some condition. An
operator either compares two pieces of data (a
binary operator) or tests one piece of data (a
unary operator). All comparison operators are
binary operators (they compare two pieces of data), as are most of the
logical operators. The only unary logical operator is the -not
operator, which returns the true/false
opposite of the data that it
tests.
Comparison operators compare two pieces of data and return a result that depends on the specific comparison operator. For example, you might want to check whether a collection has at least a certain number of elements:
PS > (dir).Count -ge 4 True
or check whether a string matches a given regular expression:
PS > "Hello World" -match "H.*World" True
Most comparison operators also adapt to the
type of their input. For example, when you apply them to simple data such as a
string, the -like
and -match
comparison operators determine whether
the string matches the specified pattern. When you apply them to a
collection of simple data, those same comparison operators return all
elements in that collection that match the pattern you provide.
Note
The -match
operator takes a regular expression
as its argument. One of the more common regular expression symbols is
the $
character, which represents the end of line.
The $
character also represents the start of a
PowerShell variable, though! To prevent PowerShell from interpreting characters as
language terms or escape sequences, place the string in single quotes
rather than double quotes:
PS > "Hello World" -match "Hello" True PS > "Hello World" -match 'Hello$' False
By default, PowerShell’s comparison operators are
case-insensitive. To use the case-sensitive versions, prefix them with
the character c
:
-ceq
,-cne
,-cge
,-cgt
,-cin
,-clt
,-cle
,-clike
,-cnotlike
,-cmatch
,-cnotmatch
,-ccontains
,-cnotcontains
For a detailed description of the comparison operators, their case-sensitive counterparts, and how they adapt to their input, see Comparison Operators.
Logical operators combine true
or false
statements and return a
result that depends on the specific logical operator. For example, you
might want to check whether a string matches the wildcard pattern you
supply and that it is longer than a certain number
of characters:
PS > $data = "Hello World" PS > ($data -like "*llo W*") -and ($data.Length -gt 10) True PS > ($data -like "*llo W*") -and ($data.Length -gt 20) False
Some of the comparison operators actually
incorporate aspects of the logical operators. Since using the opposite
of a comparison (such as -like
) is so
common, PowerShell provides comparison operators (such as -notlike
) that save you from having to use the
-not
operator
explicitly.
For a detailed description of the individual logical operators, see Comparison Operators.
Comparison operators and logical operators (when combined with flow control statements) form the core of how we write a script or command that adapts to its data and input.
See also Conditional Statements for detailed information about these statements.
For more information about PowerShell’s
operators, type Get-Help
About_Operators
.
You want to control the conditions under which PowerShell executes commands or portions of your script.
Use PowerShell’s if
, elseif
,
and else
conditional statements to
control the flow of execution in your script.
$temperature = 90 if($temperature -le 0) { "Balmy Canadian Summer" } elseif($temperature -le 32) { "Freezing" } elseif($temperature -le 50) { "Cold" } elseif($temperature -le 70) { "Warm" } else { "Hot" }
Conditional statements include the following:
if
statementExecutes the script block that follows it if its condition evaluates to
true
elseif
statementExecutes the script block that follows it if its condition evaluates to
true
and none of the conditions in theif
orelseif
statements before it evaluate totrue
else
statementExecutes the script block that follows it if none of the conditions in the
if
orelseif
statements before it evaluate totrue
In addition to being useful for script control flow, conditional statements are often a useful way to assign data to a variable. PowerShell makes this very easy by letting you assign the results of a conditional statement directly to a variable:
$result = if(Get-Process -Name notepad) { "Running" } else { "Not running" }
This technique is the equivalent of a ternary operator in other programming languages, or can form the basis of one if you’d like a more compact syntax.
For more information about these flow control statements,
type Get-
Help
About_Flow_Control
.
You want to find an easier or more compact way to represent a
large if
… elseif
… else
conditional statement.
Use PowerShell’s switch
statement to more easily represent a
large if
… elseif
… else
conditional statement.
$temperature = 20 switch($temperature) { { $_ -lt 32 } { "Below Freezing"; break } 32 { "Exactly Freezing"; break } { $_ -le 50 } { "Cold"; break } { $_ -le 70 } { "Warm"; break } default { "Hot" } }
PowerShell’s switch
statement lets you easily test its
input against a large number of comparisons. The switch
statement supports several options that
allow you to configure how PowerShell compares the input against the
conditions—such as with a wildcard, regular expression, or even an
arbitrary script block. Since scanning through the text in a file is
such a common task, PowerShell’s switch
statement supports that directly. These
additions make PowerShell switch
statements a great deal more powerful than those in C and C++.
As another example of the switch
statement in
action, consider how to determine the SKU of the current operating
system. For example, is the script running on Windows 7 Ultimate? Windows Server Cluster
Edition? The Get-CimInstance
cmdlet
lets you determine the operating
system SKU, but unfortunately returns its result as a simple number. A
switch
statement lets you map these numbers to their
English equivalents based on the official documentation listed at this site:
############################################################################## ## ## Get-OperatingSystemSku ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Gets the sku information for the current operating system .EXAMPLE PS > Get-OperatingSystemSku Professional with Media Center #> param($Sku = (Get-CimInstance Win32_OperatingSystem).OperatingSystemSku) Set-StrictMode -Version 3 switch ($Sku) { 0 { "An unknown product"; break; } 1 { "Ultimate"; break; } 2 { "Home Basic"; break; } 3 { "Home Premium"; break; } 4 { "Enterprise"; break; } 5 { "Home Basic N"; break; } 6 { "Business"; break; } 7 { "Server Standard"; break; } 8 { "Server Datacenter (full installation)"; break; } 9 { "Windows Small Business Server"; break; } 10 { "Server Enterprise (full installation)"; break; } 11 { "Starter"; break; } 12 { "Server Datacenter (core installation)"; break; } 13 { "Server Standard (core installation)"; break; } 14 { "Server Enterprise (core installation)"; break; } 15 { "Server Enterprise for Itanium-based Systems"; break; } 16 { "Business N"; break; } 17 { "Web Server (full installation)"; break; } 18 { "HPC Edition"; break; } 19 { "Windows Storage Server 2008 R2 Essentials"; break; } 20 { "Storage Server Express"; break; } 21 { "Storage Server Standard"; break; } 22 { "Storage Server Workgroup"; break; } 23 { "Storage Server Enterprise"; break; } 24 { "Windows Server 2008 for Windows Essential Server Solutions"; break; } 25 { "Small Business Server Premium"; break; } 26 { "Home Premium N"; break; } 27 { "Enterprise N"; break; } 28 { "Ultimate N"; break; } 29 { "Web Server (core installation)"; break; } 30 { "Windows Essential Business Server Management Server"; break; } 31 { "Windows Essential Business Server Security Server"; break; } 32 { "Windows Essential Business Server Messaging Server"; break; } 33 { "Server Foundation"; break; } 34 { "Windows Home Server 2011"; break; } 35 { "Windows Server 2008 without Hyper-V for Windows Essential Server Solutions"; break; } 36 { "Server Standard without Hyper-V"; break; } 37 { "Server Datacenter without Hyper-V (full installation)"; break; } 38 { "Server Enterprise without Hyper-V (full installation)"; break; } 39 { "Server Datacenter without Hyper-V (core installation)"; break; } 40 { "Server Standard without Hyper-V (core installation)"; break; } 41 { "Server Enterprise without Hyper-V (core installation)"; break; } 42 { "Microsoft Hyper-V Server"; break; } 43 { "Storage Server Express (core installation)"; break; } 44 { "Storage Server Standard (core installation)"; break; } 45 { "Storage Server Workgroup (core installation)"; break; } 46 { "Storage Server Enterprise (core installation)"; break; } 46 { "Storage Server Enterprise (core installation)"; break; } 47 { "Starter N"; break; } 48 { "Professional"; break; } 49 { "Professional N"; break; } 50 { "Windows Small Business Server 2011 Essentials"; break; } 51 { "Server For SB Solutions"; break; } 52 { "Server Solutions Premium"; break; } 53 { "Server Solutions Premium (core installation)"; break; } 54 { "Server For SB Solutions EM"; break; } 55 { "Server For SB Solutions EM"; break; } 56 { "Windows MultiPoint Server"; break; } 59 { "Windows Essential Server Solution Management"; break; } 60 { "Windows Essential Server Solution Additional"; break; } 61 { "Windows Essential Server Solution Management SVC"; break; } 62 { "Windows Essential Server Solution Additional SVC"; break; } 63 { "Small Business Server Premium (core installation)"; break; } 64 { "Server Hyper Core V"; break; } 72 { "Server Enterprise (evaluation installation)"; break; } 76 { "Windows MultiPoint Server Standard (full installation)"; break; } 77 { "Windows MultiPoint Server Premium (full installation)"; break; } 79 { "Server Standard (evaluation installation)"; break; } 80 { "Server Datacenter (evaluation installation)"; break; } 84 { "Enterprise N (evaluation installation)"; break; } 95 { "Storage Server Workgroup (evaluation installation)"; break; } 96 { "Storage Server Standard (evaluation installation)"; break; } 98 { "Windows 8 N"; break; } 99 { "Windows 8 China"; break; } 100 { "Windows 8 Single Language"; break; } 101 { "Windows 8"; break; } 103 { "Professional with Media Center"; break; } default {"UNKNOWN: " + $SKU } }
Although used as a way to express large conditional statements
more cleanly, a switch
statement
operates much like a large sequence of if
statements, as opposed to a large sequence
of if
… elseif
… elseif
… else
statements. Given the input that you
provide, PowerShell evaluates that input against
each of the comparisons in the switch
statement. If the comparison evaluates
to true
, PowerShell then executes the
script block that follows it. Unless that script block contains a break
statement, PowerShell continues to evaluate the
following comparisons.
For more information about PowerShell’s
switch
statement, see Conditional Statements or type Get-Help
About_Switch
.
Use one of PowerShell’s looping statements
(for
, foreach
, while
, and do
) or PowerShell’s Foreach-Object
cmdlet to run a command or
script block more than once. For a
detailed description of these looping statements, see Looping Statements. For example:
for
loopfor($counter = 1; $counter -le 10; $counter++) { "Loop number $counter" }
foreach
loopforeach($file in dir) { "File length: " + $file.Length }
Foreach-Object
cmdletGet-ChildItem | Foreach-Object { "File length: " + $_.Length }
while
loop$response = "" while($response -ne "QUIT") { $response = Read-Host "Type something" }
do..while
loop$response = "" do { $response = Read-Host "Type something" } while($response -ne "QUIT")
do..until
loop$response = "" do { $response = Read-Host "Type something" } until($response -eq "QUIT")
Although any of the looping statements can be written to be functionally equivalent to any of the others, each lends itself to certain problems.
You usually use a for
loop when you need to perform an operation
an exact number of times. Because using it this way is so common, it is
often called a counted for
loop.
You usually use a foreach
loop when you have a collection of
objects and want to visit each item in that collection. If you do not
yet have that entire collection in memory (as in the dir
collection from the foreach
example shown earlier), the Foreach-Object
cmdlet is usually a more
efficient alternative.
Unlike the foreach
loop, the Foreach-Object
cmdlet lets you process each
element in the collection as PowerShell generates
it. This is an important distinction; asking PowerShell to collect the entire output of
a large command (such as Get-Content
hugefile.txt
) in a
foreach
loop can easily drag down
your system.
Note
A handy shortcut to repeat an operation on the command line is:
PS > 1..10 | foreach { "Working" } Working Working Working Working Working Working Working Working Working Working
Like pipeline-oriented functions, the Foreach-Object
cmdlet lets you define commands
to execute before the looping begins, during the looping, and after the
looping completes:
PS > "a","b","c" | Foreach-Object ` -Begin { "Starting"; $counter = 0 } ` -Process { "Processing $_"; $counter++ } ` -End { "Finishing: $counter" } Starting Processing a Processing b Processing c Finishing: 3
The while
and do..while
loops are similar, in that they
continue to execute the loop as long as its condition evaluates to
true
. A while
loop checks for this before running your
script block, whereas a do..while
loop checks the condition after running your script block. A do..until
loop is exactly like a do..while
loop, except that it exits when its
condition returns $true
, rather than
when its condition returns $false
.
For a detailed description of these looping
statements, see Looping Statements or type
Get-Help About_For
, Get-Help About_Foreach
, Get-Help about_While
, or Get-Help about_Do
.
To pause until the user presses the Enter key,
use the pause
command :
PS > pause Press Enter to continue...:
To pause until the user presses any key, use
the ReadKey()
method on the $host
object:
PS > $host.UI.RawUI.ReadKey()
To pause a script for a given amount of time,
use the Start-Sleep
cmdlet:
PS > Start-Sleep 5 PS > Start-Sleep -Milliseconds 300
When you want to pause your script until the
user presses a key or for a set amount of time, pause
and Start-Sleep
are the two cmdlets you are most
likely to use.
Note
If you want to retrieve user input rather than just pause, the
Read-Host
cmdlet lets you read
input from the user. For more information, see Read a Line of User Input.
In other situations, you may sometimes want to write a loop in your script that runs at a constant speed—such as once per minute or 30 times per second. That is typically a difficult task, as the commands in the loop might take up a significant amount of time, or even an inconsistent amount of time.
In the past, many computer games suffered from solving this problem incorrectly. To control their game speed, game developers added commands to slow down their game. For example, after much tweaking and fiddling, the developers might realize that the game plays correctly on a typical machine if they make the computer count to 1 million every time it updates the screen. Unfortunately, the speed of these commands (such as counting) depends heavily on the speed of the computer. Since a fast computer can count to 1 million much more quickly than a slow computer, the game ends up running much more quickly (often to the point of incomprehensibility) on faster computers!
To make your loop run at a regular speed, you can measure how long the commands in a loop take to complete, and then delay for whatever time is left, as shown in Example 4-1.
For more information about the Start-Sleep
cmdlet, type Get-Help
Start-Sleep
.
Get Windows PowerShell Cookbook, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.