Welcome PowerShell User! This recipe is just one of the hundreds of useful resources contained in the PowerShell Cookbook.
If you own the book already, login here to get free, online, searchable access to the entire book's content.
If not, the Windows PowerShell Cookbook is available at Amazon, or any of your other favourite book retailers. If you want to see what the PowerShell Cookbook has to offer, enjoy this free 90 page e-book sample: "The Windows PowerShell Interactive Shell".
Looping statements in PowerShell let you execute groups of statements multiple times.
:loop_label
for (initialization
;condition
;increment
) {statement block
}
When PowerShell executes a for
statement, it first executes the expression given by initialization
. It next evaluates condition
. If condition
evaluates to $true
,
PowerShell executes the given statement block. It then executes the expression given by increment
. PowerShell continues to execute the statement block and increment
statement as long as condition
evaluates to $true
.
For example:
for
(
$counter
=
0
;
$counter
-lt
10
;
$counter
++)
{
Write-Host
"Processing item $counter"
}
The break
and continue
statements (discussed later in this appendix) can specify the loop_label
of any enclosing looping statement as their target.
:loop_label
foreach(variable in expression
) {statement block
}
When PowerShell executes a foreach
statement, it executes the pipeline given by expression
—for example, Get-Process | Where-Object {$_.Handles -gt 500}
or 1..10
. For each item produced by the expression, it assigns that item to the variable specified by variable
and then executes the given statement block. For example:
$handleSum
=
0
foreach
(
$process
in
Get-Process
|
Where-Object
{
$_
.
Handles
-gt
500
})
{
$handleSum
+=
$process
.
Handles
}
$handleSum
In addition to the foreach
statement, you can also use the foreach
method on collections directly:
$handleSum
=
0
(
Get-Process
).
foreach
(
{
$handleSum
+=
$_
.
Handles
}
)
The break
and continue
statements (discussed later in this appendix) can specify the loop_label
of any enclosing looping statement as their target. In addition to the
foreach
statement, PowerShell also offers the ForEach-Object
cmdlet with similar capabilities. For more information, see Recipe 4.4.
:loop_label
while(condition
) {statement block
}
When PowerShell executes a while
statement, it first evaluates the expression given by condition
. If this expression evaluates to $true
, PowerShell executes the given statement block. PowerShell continues to execute the statement block as long as condition
evaluates to $true
. For example:
$command
=
""
;
while
(
$command
-notmatch
"quit"
)
{
$command
=
Read-Host
"Enter your command"
}
The break
and continue
statements (discussed later in this appendix) can specify the loop_label
of any enclosing looping statement as their target.
:loop_label
do {statement block
} while(condition
)
or
:loop_label
do {statement block
} until(condition
)
When PowerShell executes a do … while
or do … until
statement, it first executes the given statement block. In a do … while
statement, PowerShell continues to execute the statement block as long as condition
evaluates to $true
. In a do … until
statement, PowerShell continues to execute the statement as long as condition
evaluates to $false
. For example:
$validResponses
=
"Yes"
,
"No"
$response
=
""
do
{
$response
=
Read-Host
"Yes or No?"
}
while
(
$validResponses
-notcontains
$response
)
"Got it."
$response
=
""
do
{
$response
=
Read-Host
"Yes or No?"
}
until
(
$validResponses
-contains
$response
)
"Got it."
The break
and continue
statements (discussed later in this appendix) can specify the loop_label
of any enclosing looping statement as their target.
PowerShell supports two statements to help you control flow within loops: break
and continue
.
The break
statement halts execution of the current loop. PowerShell then resumes execution at the end of the current looping statement, as though the looping statement had completed naturally. For example:
for
(
$counter
=
0
;
$counter
-lt
5
;
$counter
++)
{
for
(
$counter2
=
0
;
$counter2
-lt
5
;
$counter2
++)
{
if
(
$counter2
-eq
2
)
{
break
}
Write-Host
"Processing item $counter,$counter2"
}
}
produces the output (notice the second column never reaches the value 2
):
Processing item 0,0 Processing item 0,1 Processing item 1,0 Processing item 1,1 Processing item 2,0 Processing item 2,1 Processing item 3,0 Processing item 3,1 Processing item 4,0 Processing item 4,1
If you specify a label with the break
statement—for example, break outer_loop
—PowerShell halts the execution of that loop instead. For example:
:
outer_loop
for
(
$counter
=
0
;
$counter
-lt
5
;
$counter
++)
{
for
(
$counter2
=
0
;
$counter2
-lt
5
;
$counter2
++)
{
if
(
$counter2
-eq
2
)
{
break
outer_loop
}
Write-Host
"Processing item $counter,$counter2"
}
}
produces the output:
Processing item 0,0 Processing item 0,1
The continue
statement skips execution of the rest of the current statement block. PowerShell then continues with the next iteration of the current looping statement, as though the statement block had completed naturally. For example:
for
(
$counter
=
0
;
$counter
-lt
5
;
$counter
++)
{
for
(
$counter2
=
0
;
$counter2
-lt
5
;
$counter2
++)
{
if
(
$counter2
-eq
2
)
{
continue
}
Write-Host
"Processing item $counter,$counter2"
}
}
produces the output:
Processing item 0,0 Processing item 0,1 Processing item 0,3 Processing item 0,4 Processing item 1,0 Processing item 1,1 Processing item 1,3 Processing item 1,4 Processing item 2,0 Processing item 2,1 Processing item 2,3 Processing item 2,4 Processing item 3,0 Processing item 3,1 Processing item 3,3 Processing item 3,4 Processing item 4,0 Processing item 4,1 Processing item 4,3 Processing item 4,4
If you specify a label with the continue
statement—for example, continue outer_loop
—PowerShell continues with the next iteration of that loop instead.
For example:
:
outer_loop
for
(
$counter
=
0
;
$counter
-lt
5
;
$counter
++)
{
for
(
$counter2
=
0
;
$counter2
-lt
5
;
$counter2
++)
{
if
(
$counter2
-eq
2
)
{
continue
outer_loop
}
Write-Host
"Processing item $counter,$counter2"
}
}
produces the output:
Processing item 0,0 Processing item 0,1 Processing item 1,0 Processing item 1,1 Processing item 2,0 Processing item 2,1 Processing item 3,0 Processing item 3,1 Processing item 4,0 Processing item 4,1
## A class called "Example" that inherits from "BaseClass"
## and implements the "ImplementedInterface" interface
class
Example
:
BaseClass
,
ImplementedInterface
{
## Default constructor, which also invokes the constructor
## from the base class.
Example
()
:
base
()
{
[Example]
::
lastInstantiated
=
Get-Date
}
## Constructor with parameters
Example
(
[string]
$Name
)
{
$this
.
Name
=
$Name
[Example]
::
lastInstantiated
=
Get-Date
}
## A publicly visible property with validation attributes
[
ValidateLength
(
2
,
20
)]
[string]
$Name
## A property that is hidden from default views
static
hidden
[DateTime]
$lastInstantiated
## A publicly visible method that returns a value
[string]
ToString
()
{
## Return statement is required. Implicit / pipeline output
## is not treated as output like it is with functions.
return
$this
.
ToString
(
[Int32]
::
MaxValue
)
}
## A publicly visible method that returns a value
[string]
ToString
(
[int]
$MaxLength
)
{
$output
=
"Name =
$(
$this
.
Name
)
;"
+
"LastInstantiated =
$(
[Example]
::
lastInstantiated
)
"
$outputLength
=
[Math]
::
Min
(
$MaxLength
,
$output
.
Length
)
return
$output
.
Substring
(
0
,
$outputLength
)
}
}
To define a class that inherits from a base class or implements an interfaces, provide the base class and/or interface names after the class name, separated by a colon. Deriving from a base class or implementing any interfaces is optional.
class Example [: BaseClass, ImplementedInterface]
To define a class constructor, create a method with the same name as the class. You can define several constructors, including those with parameters. To automatically call a constructor from the base class, add : base()
to the end of the method name.
Example() [: base()] Example([int] $Parameter1, [string] $Parameter2) [: base()]
To define a publicly visible property, define a PowerShell variable in your class. As with regular Powershell variables, you may optionally add validation attributes or declare a type constraint for the property.
[ValidateLength(2,20)] [string] $Name
To hide the property from default views (similar to a member variable in other languages), use the hidden
keyword. Users are still able to access hidden properties if desired: they are just removed from default views. You can make a property static
if you want it to be shared with all instances of your class in the current process.
static hidden [DateTime] $lastInstantiated
Define a method as though you would define a PowerShell function, but without the function
keyword and without the param()
statement. Methods support parameters, parameter validation, and can also have the same name as long as their parameters differ.
[string] ToString() { ... } [string] ToString([int] $MaxLength) { ... }
To define a custom enumeration, use the enum
keyword:
enum
MyColor
{
Red
=
1
Green
=
2
Blue
=
3
}
If enumeration values are intended to be combined through bitwise operators, use the [Flags()]
attribute. If you require that the enumerated values derive from a specific integral data type (byte
, sbyte
, short
, ushort
, int
, uint
, long
or ulong
), provide that data type after the colon character.
[
Flags
()]
enum
MyColor
:
uint
{
Red
=
1
Green
=
2
Blue
=
4
}
Within a workflow, PowerShell supports three statements not supported in traditional PowerShell scripts: InlineScript
, Parallel
, and Sequence
.
Workflows are no longer supported in PowerShell. This section exists to help you understand and work with workflows that have already been written.
The InlineScript
keyword defines an island of PowerShell script that will be invoked as a unit, and with traditional PowerShell scripting semantics. For example:
workflow
MyWorkflow
{
## Method invocation not supported in a workflow
## [Math]::Sqrt(100)
InlineScript
{
## Supported in an InlineScript
[Math]
::
Sqrt
(
100
)
}
}
The Parallel
keyword specifies that all statements within the statement block should run in parallel. To group statements that should be run as a unit, use the Sequence
keyword:
workflow
MyWorkflow
{
Parallel
{
InlineScript
{
Start-Sleep
-Seconds
2
;
"One thing run in parallel"
}
InlineScript
{
Start-Sleep
-Seconds
4
;
"Another thing run in parallel"
}
InlineScript
{
Start-Sleep
-Seconds
3
;
"A third thing run in parallel"
}
Sequence
{
Start-Sleep
-Seconds
1
"A fourth"
"and fifth thing run as a unit, in parallel"
}
}
}
Note that you should not use PowerShell Workflows for the parallel statement
alone—the -Parallel
parameter to the ForEach-Object
cmdlet is much more efficient.