Chapter 1. PowerShell Language and Environment
Commands and Expressions
PowerShell breaks any line that you enter into its individual units (tokens), and then interprets each token in one of two ways: as a command or as an expression. The difference is subtle: expressions support logic and flow control statements (such as if
, foreach
, and throw
), whereas commands do not.
You will often want to control the way that PowerShell interprets your statements, so Table 1-1 lists the available options.
Comments
To create single-line comments, begin a line with the #
character. To create a block (or multiline) comment, surround the region with the characters <#
and #>
:
# This is a regular comment
<# This is a block comment
function MyTest
{
"This should not be considered a function"
}
$myVariable = 10;
Block comment ends
#>
# This is regular script again
Help Comments
PowerShell creates help for your script or function by looking at its comments. If the comments include any supported help tags, PowerShell adds those to the help for your command.
Comment-based help supports the following tags, which are all case-insensitive:
.SYNOPSIS
-
A short summary of the command, ideally a single sentence.
.DESCRIPTION
-
A more detailed description of the command.
.PARAMETER
name
-
A description of parameter
name
, with one for each parameter you want to describe. While you can write a.PARAMETER
comment for each parameter, PowerShell also supports comments written directly above the parameter. Putting parameter help alongside the actual parameter makes it easier to read and maintain. .EXAMPLE
-
An example of this command in use, with one for each example you want to provide. PowerShell treats the line immediately beneath the
.EXAMPLE
tag as the example command. If this line doesn’t contain any text that looks like a prompt, PowerShell adds a prompt before it. It treats lines that follow the initial line as additional output and example commentary. .INPUTS
-
A short summary of pipeline input(s) supported by this command. For each input type, PowerShell’s built-in help follows this convention:
System.String You can pipe a string that contains a path to Get-ChildItem.
.OUTPUTS
-
A short summary of items generated by this command. For each output type, PowerShell’s built-in help follows this convention:
System.ServiceProcess.ServiceController This cmdlet returns objects that represent the services on the computer.
.NOTES
-
Any additional notes or remarks about this command.
.LINK
-
A link to a related help topic or command, with one
.LINK
tag per link. If the related help topic is a URL, PowerShell launches that URL when the user supplies the-Online
parameter toGet-Help
for your command.
While these are all of the supported help tags you are likely to use, comment-based help also supports tags for some of
Get-Help
’s more obscure features:
-
.COMPONENT
-
.ROLE
-
.FUNCTIONALITY
-
.FORWARDHELPTARGETNAME
-
.FORWARDHELPCATEGORY
-
.REMOTEHELPRUNSPACE
-
.EXTERNALHELP
For more information about these tags, type Get-Help about_Comment_Based_Help
.
Variables
PowerShell provides several ways to define and access variables, as summarized in Table 1-2.
Booleans
Boolean (true or false) variables are most commonly initialized to their literal values of $true
and $false
. When PowerShell evaluates variables as part of a Boolean expression (for example, an if
statement), though, it maps them to a suitable Boolean representation, as listed in Table 1-3.
Result | Boolean representation |
---|---|
|
True |
|
False |
|
False |
Nonzero number |
True |
Zero |
False |
Nonempty string |
True |
Empty string |
False |
Empty array |
False |
Single-element array |
The Boolean representation of its single element |
Multi-element array |
True |
Hashtable (either empty or not) |
True |
Strings
PowerShell offers several facilities for working with plain-text data.
Literal and Expanding Strings
To define a literal string (one in which no variable or escape expansion occurs), enclose it in single quotes:
$myString
=
'hello `t $ENV:SystemRoot'
$myString
gets the actual value of hello `t $ENV:SystemRoot
.
To define an expanding string (one in which variable and escape expansion occur), enclose it in double quotes:
$myString
=
"hello
`t
$ENV:SystemRoot"
$myString
gets a value similar to hello C:\WINDOWS
.
To include a single quote in a single-quoted string or a double quote in a double-quoted string, include two of the quote characters in a row:
PS > "Hello ""There""!" Hello "There"! PS > 'Hello ''There''!' Hello 'There'!
Note
To include a complex expression inside an expanding string, use a subexpression. For example:
$prompt
=
"
$(
Get-Location
)
>"
$prompt
gets a value similar to c:\temp >
.
Accessing the properties of an object requires a subexpression:
$version
=
"Current PowerShell version is:"
$PSVersionTable
.
PSVersion
.
Major
$version
gets a value similar to:
Current PowerShell version is: 3
Here Strings
To define a here string (one that may span multiple lines), place the two characters @"
at the beginning and the two characters "@
on their own line at the end.
For example:
$myHereString
=
@"
This text may span multiple lines, and may
contain "quotes."
"@
Here strings may be of either the literal (single-quoted) or expanding (double-quoted) variety.
Escape Sequences
PowerShell supports escape sequences inside strings, as listed in Table 1-4.
Sequence | Meaning |
---|---|
|
The null character. Often used as a record separator. |
|
The alarm character. Generates a beep when displayed on the console. |
|
The backspace character. The previous character remains in the string but is overwritten when displayed on the console. |
|
The escape character. Marks the beginning of an ANSI escape sequence such as " |
|
A form feed. Creates a page break when printed on most printers. |
|
A newline. |
|
A carriage return. Newlines in PowerShell are indicated entirely by the |
|
A tab. |
|
A unicode character literal. Creates a character represented by the specified hexadecimal Unicode code point, such as |
|
A vertical tab. |
|
A single quote, when in a literal string. |
|
A double quote, when in an expanding string. |
|
Numbers
PowerShell offers several options for interacting with numbers and numeric data.
Simple Assignment
To define a variable that holds numeric data, simply assign it as you would other variables. PowerShell automatically stores your data in a format that is sufficient to accurately hold it:
$myInt
=
10
$myUnsignedInt
=
10u
$myUnsignedInt
=
[uint]
10
$myInt
gets the value of 10
, as a (32-bit) integer. $myUnsignedInt
gets the value of 10
as an unsigned integer.
$myDouble
=
3
.
14
$myDouble
gets the value of 3.14
, as a (53-bit, 9 bits of precision) double.
To explicitly assign a number as a byte (8-bit) or short (16-bit) number, use the y
or s
suffixes. Prefixing either with u
creates an unsigned version of that data type. You can also use the [byte]
, [int16]
, and [short]
casts:
$myByte
=
127y
$myByte
=
[byte]
127
$myUnsignedByte
=
127uy
$myShort
=
32767s
$myShort
=
[int16]
32767
$myShort
=
[short]
32767
$myUnsignedShort
=
32767us
$myUnsignedShort
=
[ushort]
32767
To explicitly assign a number as a long (64-bit) integer or decimal (96-bit, 96 bits of precision), use the long (l
) and decimal (d
) suffixes. You can also use the [long]
cast:
$myLong
=
2147483648l
$myLong
=
[long]
2147483648
$myUnsignedLong
=
2147483648ul
$myUnsignedLong
=
[ulong]
2147483648
$myDecimal
=
0
.
999d
To explicitly assign a number as a BigInteger (an arbitrary large integer with no upper or lower bounds), use the BigInteger (n
) suffix:
$myBigInt
=
99999999999999999999999999999n
PowerShell also supports scientific notation, where e
<number
> represents multiplying the original number by the <number
> power of 10:
$myPi
=
3141592653e
-
9
$myPi
gets the value of 3.141592653
.
The data types in PowerShell (integer, long integer, double, and decimal) are built on the .NET data types of the same names.
Administrative Numeric Constants
Since computer administrators rarely get the chance to work with numbers in even powers of 10, PowerShell offers the numeric constants of pb
, tb
, gb
, mb
, and kb
to represent petabytes (1,125,899,906,842,624), terabytes (1,099,511,627,776), gigabytes (1,073,741,824), megabytes (1,048,576), and kilobytes (1,024), respectively:
PS > $downloadTime = (1gb + 250mb) / 120kb PS > $downloadTime 10871.4666666667
You can combine these numeric multipliers with a data type as long as the result fits in that data type, such as 250ngb
.
Hexadecimal and Other Number Bases
To directly enter a hexadecimal number, use the hexadecimal prefix 0x
:
$myErrorCode
=
0xFE4A
$myErrorCode
gets the integer value 65098
.
To directly enter a binary number, use the binary prefix 0b
:
$myBinary
=
0b101101010101
$myBinary
gets the integer value of 2901
.
If you don’t know the hex or binary value as a constant or need to convert into Octal, use the [Convert]
class from the .NET Framework. The first parameter is the value to convert, and the second parameter is the base (2, 8, 10, or 16):
$myOctal
=
[Convert]
::
ToInt32
(
"1234567"
,
8
)
$myOctal
gets the integer value of 342391
.
$myHexString
=
[Convert]
::
ToString
(
65098
,
16
)
$myHexString
gets the string value of fe4a
.
$myBinaryString
=
[Convert]
::
ToString
(
12345
,
2
)
$myBinaryString
gets the string value of 11000000111001
.
Note
See “Working with the .NET Framework” to learn more about using PowerShell to interact with the .NET Framework.
Large Numbers
To work with extremely large numbers, use the BigInt
class.
[BigInt]
::
Pow
(
12345
,
123
)
To do math with several large numbers, use the [BigInt]
cast (or the n
BigInt data type) for all operands:
PS > 98123498123498123894n * 98123498123498123894n 9628220883992139841085109029337773723236 PS > $val = "98123498123498123894" PS > ([BigInt] $val) * ([BigInt] $val) 9628220883992139841085109029337773723236
Arrays and Lists
Array Definitions
PowerShell arrays hold lists of data. The @()
(array cast) syntax tells PowerShell to treat the contents between the parentheses as an array. To create an empty array, type:
$myArray
=
@()
To define a nonempty array, use a comma to separate its elements:
$mySimpleArray
=
1
,
"Two"
,
3
.
14
Arrays may optionally be only a single element long:
$myList
=
,
"Hello"
Or, alternatively (using the array cast syntax):
$myList
=
@(
"Hello"
)
Elements of an array don’t need to be all of the same data type, unless you declare it as a strongly typed array. In the following example, the outer square brackets define a strongly typed variable (as mentioned in “Variables”), and int[]
represents an array of integers:
[int[]]
$myArray
=
1
,
2
,
3
.
14
In this mode, PowerShell generates an error if it cannot convert any of the elements in your list to the required data type. In this case, it rounds 3.14
to the integer value of 3:
PS > $myArray[2] 3
Note
To ensure that PowerShell treats collections of uncertain length (such as history lists or directory listings) as a list, use the list evaluation syntax @(…)
described in “Commands and Expressions”.
Arrays can also be multidimensional jagged arrays (arrays within arrays):
$multiDimensional
=
@(
(
1
,
2
,
3
,
4
),
(
5
,
6
,
7
,
8
)
)
$multiDimensional[0][1]
returns 2, coming from row 0,
column 1.
$multiDimensional[1][3]
returns 8, coming from row 1,
column 3.
To define a multidimensional array that is not jagged, create a multidimensional instance of the .NET type. For integers, that would be an array of System.Int32
:
$multidimensional
=
New-Object
"Int32[,]"
2
,
4
$multidimensional
[
0
,
1
]
=
2
$multidimensional
[
1
,
3
]
=
8
Array Access
To access a specific element in an array, use the []
operator. PowerShell numbers your array elements starting at zero. Using $myArray = 1,2,3,4,5,6
as an example:
$myArray
[
0
]
returns 1, the first element in the array.
$myArray
[
2
]
returns 3, the third element in the array.
$myArray
[-
1
]
returns 6, the last element of the array.
$myArray
[-
2
]
returns 5, the second-to-last element of the array.
You can also access ranges of elements in your array:
PS > $myArray[0..2] 1 2 3
returns elements 0 through 2, inclusive.
PS > $myArray[-1..2] 6 1 2 3
returns the final element, wraps around, and returns elements 0 through 2, inclusive. PowerShell wraps around because the first number in the range is negative, and the second number in the range is positive.
PS > $myArray[-1..-3] 6 5 4
returns the last element of the array through to the third-to-last element in the array, in descending order. PowerShell does not wrap around (and therefore scans backward in this case) because both numbers in the range share the same sign.
If the array being accessed might be null, you can use the null conditional array access operator (?[]
). The result of the expression will be null if the array being accessed did not exist. It will be the element at the specified index otherwise:
(
Get-Process
-id
0
).
Modules
?[
0
]
Array Slicing
You can combine several of the statements in the previous section at once to extract more complex ranges from an array. Use the + sign to separate array ranges from explicit indexes:
$myArray
[
0
,
2
,
4
]
returns the elements at indices 0, 2, and 4.
$myArray
[
0
,
2
+
4
..
5
]
returns the elements at indices 0, 2, and 4 through 5, inclusive.
$myArray
[,
0
+
2
..
3
+
0
,
0
]
returns the elements at indices 0, 2 through 3 inclusive, 0, and 0 again.
Note
You can use the array slicing syntax to create arrays as well:
$myArray
=
,
0
+
2
..
3
+
0
,
0
Hashtables (Associative Arrays)
Hashtable Definitions
PowerShell hashtables (also called associative arrays) let you associate keys with values. To define a hashtable, use the syntax:
$myHashtable
=
@{}
You can initialize a hashtable with its key/value pairs when you create it. PowerShell assumes that the keys are strings, but the values may be any data type:
$myHashtable
=
@{
Key1
=
"Value1"
;
"Key 2"
=
1
,
2
,
3
;
3
.
14
=
"Pi"
}
To define a hashtable that retains its insertion order, use the [ordered]
cast:
$orderedHash
=
[ordered]
@{}
$orderedHash
[
"NewKey"
]
=
"Value"
Hashtable Access
To access or modify a specific element in an associative array, you can use either the array-access or property-access syntax:
$myHashtable
[
"Key1"
]
returns "Value1"
.
$myHashtable
.
"Key 2"
returns the array 1,2,3
.
$myHashtable
[
"New Item"
]
=
5
adds "New Item
" to the hashtable.
$myHashtable
.
"New Item"
=
5
also adds "New Item
" to the hashtable.
XML
PowerShell supports XML as a native data type. To create an XML variable, cast a string to the [xml]
type:
$myXml = [xml] @" <AddressBook> <Person contactType="Personal"> <Name>Lee</Name> <Phone type="home">555-1212</Phone> <Phone type="work">555-1213</Phone> </Person> <Person contactType="Business"> <Name>Ariel</Name> <Phone>555-1234</Phone> </Person> </AddressBook> "@
PowerShell exposes all child nodes and attributes as properties. When it does this, PowerShell automatically groups children that share the same node type:
$myXml
.
AddressBook
returns an object that contains a Person
property.
$myXml
.
AddressBook
.
Person
returns a list of Person
nodes. Each person node exposes contactType
, Name
, and Phone
as properties.
$myXml
.
AddressBook
.
Person
[
0
]
returns the first Person
node.
$myXml
.
AddressBook
.
Person
[
0
].
ContactType
returns Personal
as the contact type of the first Person
node.
Simple Operators
Once you have defined your data, the next step is to work with it.
Arithmetic Operators
The arithmetic operators let you perform mathematical operations on your data, as shown in Table 1-5.
Note
The System.Math
class in the .NET Framework offers many powerful operations in addition to the native operators supported by PowerShell:
PS > [Math]::Pow([Math]::E, [Math]::Pi) 23.1406926327793
See “Working with the .NET Framework” to learn more about using PowerShell to interact with the .NET Framework.
Logical Operators
The logical operators let you compare Boolean values, as shown in Table 1-6.
Binary Operators
The binary operators, listed in Table 1-7, let you apply the Boolean logical operators bit by bit to the operator’s arguments. When comparing bits, a 1 represents $true
, whereas a 0 represents $false
.
Other Operators
PowerShell supports several other simple operators, as listed here.
-replace (Replace operator)
The replace operator returns a new string, where the text in "target"
that matches the regular expression "pattern"
has been replaced with the replacement text "replacement"
:
"target"
-replace"pattern","replacement"
The following returns a new string, where the text in "target"
that matches the regular expression "pattern"
has been replaced with the output value of the script block supplied. In the script block, the $_
variable represents the current
System.Text.RegularExpressions.Match
:
"target"
-replace"pattern",{ scriptblock }
By default, PowerShell performs a case-insensitive comparison. The -ireplace
operator makes this case-insensitivity explicit, whereas the -creplace
operator performs a case-sensitive
comparison.
If the regular expression pattern contains named captures or capture groups, the replacement string may reference those as well. For example:
PS > "Hello World" -replace "(.*) (.*)",'$2 $1' World Hello
If "target"
represents an array, the -replace
operator operates on each element of that array.
For more information on the details of regular expressions, see Chapter 2.
-f (Format operator)
The format operator returns a string where the format items in the format string have been replaced with the text equivalent of the values in the value array:
"Format String"
-fvalues
For example:
PS > "{0:n0}" -f 1000000000 1,000,000,000
The format string for the format operator is exactly the format string supported by the .NET String.Format
method.
For more details about the syntax of the format string, see Chapter 4.
-split (Split operator)
The unary split operator breaks the given input string into an array, using whitespace (\s+
) to identify the boundary between elements:
-split "Input String"
It also trims the results. For example:
PS > -split " Hello World " Hello World
The binary split operator breaks the given input string into an array, using the given
delimiter
or script block
to identify the boundary between elements:
"Input String" -split "delimiter
",maximum
,options
"Input String" -split {Scriptblock
},maximum
Delimiter
is interpreted as a regular expression match. Scriptblock
is called for each character in the input, and a split is introduced when it returns $true
.
Maximum
defines the maximum number of elements to be returned, leaving unsplit elements as the last item. This item is optional. Use "0
" for unlimited if you want to provide options but not alter the maximum.
Options
define special behavior to apply to the splitting behavior. The possible enumeration values are:
SimpleMatch
-
Split on literal strings, rather than regular expressions they may represent.
RegexMatch
-
Split on regular expressions. This option is the default.
CultureInvariant
-
Does not use culture-specific capitalization rules when doing a case-insensitive split.
IgnorePatternWhitespace
-
Ignores spaces and regular expression comments in the split pattern.
Multiline
-
Allows the
^
and$
characters to match line boundaries, not just the beginning and end of the content. Singleline
-
Treats the
^
and$
characters as the beginning and end of the content. This option is the default. IgnoreCase
-
Ignores the capitalization of the content when searching for matches.
ExplicitCapture
-
In a regular expression match, only captures named groups. This option has no impact on the
-split
operator.
For example:
PS > "1a2B3" -split "[a-z]+",0,"IgnoreCase" 1 2 3
-join (Join operator)
The unary join operator combines the supplied items into a single string, using no separator:
-join ("item1","item2",...,"item_n")
For example:
PS > -join ("a","b") ab
The binary join operator combines the supplied items into a single string, using Delimiter
as the separator:
("item1","item2",...,"item_n")
-joinDelimiter
For example:
PS > ("a","b") -join ", " a, b
Comparison Operators
The PowerShell comparison operators, listed in Table 1-8, let you compare expressions against each other. By default,
PowerShell’s comparison operators are case-insensitive. For all operators where case sensitivity applies, the -i
prefix makes this case insensitivity explicit, whereas the -c
prefix performs a case-sensitive comparison.
Operator | Meaning |
---|---|
|
The equality operator:
For all primitive types, returns When used with arrays, returns all elements in
When used with any other type, PowerShell uses that type’s |
|
The negated equality operator:
For all primitive types, returns When used with arrays, returns all elements in
When used with any other type, PowerShell returns the negation of that type’s |
|
The greater-than-or-equal operator:
For all primitive types, returns When used with arrays, returns all elements in
When used with any other type, PowerShell returns the result of that object’s |
|
The greater-than operator:
For all primitive types, returns When used with arrays, returns all elements in
When used with any other type, PowerShell returns the result of that object’s |
|
|
|
|
|
The less-than operator:
For all primitive types, returns When used with arrays, returns all elements in
When used with any other type, PowerShell returns the result of that object’s |
|
The less-than-or-equal operator:
For all primitive types, returns When used with arrays, returns all elements in
When used with any other type, PowerShell returns the result of that object’s |
|
The like operator:
Evaluates the pattern against the target, returning When used with arrays, returns all elements in
The
For example: PS > "Test" -like "[A-Z]e?[tr]" True |
|
The match operator:
Evaluates the regular expression against the target, returning When used with arrays, returns all elements in The For example: PS > "Hello World" -match "(.*) (.*)" True PS > $matches[1] Hello For more information on the details of regular expressions, see Chapter 2. |
|
|
|
|
|
|
|
|
|
Conditional Statements
Conditional statements in PowerShell let you change the flow of execution in your script.
if, elseif, and else Statements
if(condition)
{ statement block } elseif(condition)
{ statement block } else { statement block }
If condition
evaluates to $true
, PowerShell executes the statement block you provide. Then, it resumes execution at the end of the if/elseif/else
statement list. PowerShell requires the enclosing braces around the statement block, even if the statement block contains only one statement.
Note
See “Simple Operators” and “Comparison Operators” for discussion on how PowerShell evaluates expressions as conditions.
If condition
evaluates to $false
, PowerShell evaluates any following (optional) elseif
conditions until one matches. If one matches, PowerShell executes the statement block associated with that condition, and then resumes execution at the end of the
if/elseif/else
statement list.
For example:
$textToMatch
=
Read-Host
"Enter some text"
$matchType
=
Read-Host
"Apply Simple or Regex matching?"
$pattern
=
Read-Host
"Match pattern"
if
(
$matchType
-eq
"Simple"
)
{
$textToMatch
-like
$pattern
}
elseif
(
$matchType
-eq
"Regex"
)
{
$textToMatch
-match
$pattern
}
else
{
Write-Host
"Match type must be Simple or Regex"
}
If none of the conditions evaluate to $true
, PowerShell executes the statement block associated with the (optional) else
clause, and then resumes execution at the end of the
if/elseif/else
statement list.
To apply an if
statement to each element of a list and filter it to return only the results that match the supplied condition, use the Where-Object
cmdlet or .where()
method:
Get-Process
|
Where-Object
{
$_
.
Handles
-gt
500
}
(
Get-Process
).
where
(
{
$_
.
Handles
-gt
500
}
)
Ternary Operators
$result
=condition
?true value
:false value
A short-form version of an if/else statement. If condition
evaluates to $true
, the result of the expression is the value of the true value clause. Otherwise, the result of the expression is the value of the false value clause. For example:
(
Get-Random
)
%
2
-eq
0
?
"Even number"
:
"Odd number"
Null Coalescing and Assignment Operators
$result
=nullable value
??default value
$result
=nullable value
$result
??=default value
A short-form version of a ternary operator that only checks if the expression is null or not. If it is null, the result of the expression is the value of the default value clause. For example:
Get-Process
|
ForEach
-Object
{
$_
.
CPU
??
"<Unavailable>"
}
or
$cpu
=
(
Get-Process
-id
0
).
CPU
$cpu
??=
"Unavailable"
switch Statements
switchoptions expression
{comparison value
{statement block
} -or- {comparison expression
} {statement block
} (...) default {statement block
} }
or:
switchoptions
-filefilename
{comparison value
{statement block
} -or {comparison expression
} {statement block
} (...) default {statement block
} }
When PowerShell evaluates a switch
statement, it evaluates expression
against the statements in the switch body. If
expression
is a list of values, PowerShell evaluates each item against the statements in the switch body. If you specify the
-file
option, PowerShell treats the lines in the file as though they were a list of items in
expression
.
The comparison value
statements let you match the current input item against the pattern specified by comparison value
. By default, PowerShell treats this as a case-insensitive exact match, but the options you provide to the switch
statement can change this, as shown in Table 1-9.
The {
comparison expression
}
statements let you process the current input item, which is stored in the $_
(or $PSItem
)
variable, in an arbitrary script block. When it processes a
{
comparison expression
}
statement, PowerShell executes the associated statement block only if {
comparison expression
}
evaluates to $true
.
PowerShell executes the statement block associated with the (optional) default
statement if no other statements in the switch
body match.
When processing a switch
statement, PowerShell tries to match the current input object against each statement in the switch
body, falling through to the next statement even after one or more have already matched. To have PowerShell discontinue the current comparison (but retry the switch statement with the next input object), include a continue
statement as the last statement in the statement block. To have PowerShell exit a switch
statement completely after it processes a match, include a break
statement as the last statement in the statement block.
For example:
$myPhones
=
"(555) 555-1212"
,
"555-1234"
switch
-regex
(
$myPhones
)
{
{
$_
.
Length
-le
8
}
{
"Area code was not specified"
;
break
}
{
$_
.
Length
-gt
8
}
{
"Area code was specified"
}
"\((555)\).*"
{
"In the
$(
$matches
[
1
])
area code"
}
}
produces the output:
Area code was specified In the 555 area code Area code was not specified
Note
See the next section on Looping Statements for more information about the break
statement.
By default, PowerShell treats this as a case-insensitive exact match, but the options you provide to the switch
statement can change this.
Looping Statements
Looping statements in PowerShell let you execute groups of statements multiple times.
for Statement
: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 in “Flow Control Statements”) can specify the loop_label
of any enclosing looping statement as their target.
foreach Statement
: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 in “Flow Control Statements”) 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.
while Statement
: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 in “Flow Control Statements”) can specify the loop_label
of any enclosing looping statement as their target.
do … while Statement/do … until Statement
: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 $response"
$response
=
""
do
{
$response
=
Read-Host
"Yes or No?"
}
until
(
$validResponses
-contains
$response
)
"Got $response"
The break
and continue
statements (discussed in the next section) can specify the loop_label
of any enclosing looping statement as their target.
Flow Control Statements
PowerShell supports two statements to help you control flow within loops: break
and continue
.
break
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
continue
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"
}
}
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
Classes
## 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
)
}
}
Base classes and interfaces
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]
Constructors
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()]
Properties
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
Methods
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) { ... }
Custom Enumerations
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
}
Workflow-Specific Statements
Within a workflow, PowerShell supports three statements not supported in traditional PowerShell scripts: InlineScript
,
Parallel
, and Sequence
.
Note
Workflows are no longer supported in PowerShell. This section exists to help you understand and interact with workflows that have already been written.
InlineScript
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
)
}
}
Parallel/Sequence
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.
Working with the .NET Framework
One feature that gives PowerShell its incredible reach into both system administration and application development is its capability to leverage Microsoft’s enormous and broad .NET Framework.
Working with the .NET Framework in PowerShell comes mainly by way of one of two tasks: calling methods or accessing properties.
Static Methods
To call a static method on a class, type:
[ClassName
]::MethodName
(parameter list
)
For example:
PS > [System.Diagnostics.Process]::GetProcessById(0)
gets the process with the ID of 0 and displays the following output:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 0 0 0 16 0 0 Idle
Instance Methods
To call a method on an instance of an object, type:
$objectReference
.MethodName
(parameter list
)
For example:
PS > $process = [System.Diagnostics.Process]::GetProcessById(0) PS > $process.Refresh()
This stores the process with ID of 0 into the $process
variable. It then calls the Refresh()
instance method on that specific process.
Static Properties
To access a static property on a class, type:
[ClassName
]::PropertyName
or:
[ClassName
]::PropertyName
=value
For example, the [System.DateTime]
class provides a Now
static property that returns the current time:
PS > [System.DateTime]::Now Sunday, July 16, 2006 2:07:20 PM
Although this is rare, some types let you set the value of some static properties.
Instance Properties
To access an instance property on an object, type:
$objectReference
.PropertyName
or:
$objectReference
.PropertyName
=value
For example:
PS > $today = [System.DateTime]::Now PS > $today.DayOfWeek Sunday
This stores the current date in the $today
variable. It then calls the DayOfWeek
instance property on that specific date.
If the value of the property might be null, you can use the null conditional property access operator (?.
). The result of the expression will be null if any property in the chain did not exist. It will be the final property’s value otherwise:
(
Get-Process
-id
0
)?.
MainModule
?.
Filename
Learning About Types
The two primary avenues for learning about classes and types are the Get-Member
cmdlet and the documentation for the .NET Framework.
The Get-Member cmdlet
To learn what methods and properties a given type supports, pass it through the Get-Member
cmdlet, as shown in Table 1-10.
Action | Result |
---|---|
|
All the static methods and properties of a given type. |
|
All the static methods and properties provided by the type in |
|
All the instance methods and properties provided by the type in Get-Member -InputObject $ |
|
All the instance methods and properties of a
|
.NET Framework documentation
Another source of information about the classes in the .NET Framework is the documentation itself, available through the search facilities at Microsoft’s developer documentation site.
Typical documentation for a class first starts with a general overview, and then provides a hyperlink to the members of the class—the list of methods and properties it supports.
Note
To get to the documentation for the members quickly, search for them more explicitly by adding the term “members” to your search term:
classname
members
The documentation for the members of a class lists their constructors, methods, properties, and more. It uses an S icon to represent the static methods and properties. Click the member name for more information about that member, including the type of object that the member produces.
Type Shortcuts
When you specify a type name, PowerShell lets you use a short form for some of the most common types, as listed in Table 1-11.
Type shortcut | Full classname |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Creating Instances of Types
$objectReference
= New-ObjectTypeName
parameters
$objectReference
= [TypeName
]::new(parameters
)
Although static methods and properties of a class generate objects, you’ll often want to create them explicitly yourself. PowerShell’s New-Object
cmdlet lets you create an instance of the type you specify. The parameter list must match the list of parameters accepted by one of the type’s constructors, as described in the SDK documentation.
For example:
$webClient
=
New-Object
Net
.
WebClient
$webClient
.
DownloadString
(
"http://search.msn.com"
)
If the type represents a generic type, enclose its type parameters in square brackets:
PS > $hashtable = New-Object "System.Collections.Generic.Dictionary[String,Bool]" PS > $hashtable["Test"] = $true
Most common types are available by default. However, many types are available only after you load the library (called the assembly) that defines them. The Microsoft documentation for a class includes the assembly that defines it.
To load an assembly, use the -AssemblyName
parameter of the Add-Type
cmdlet:
PS > Add-Type -AssemblyName System.Web PS > [System.Web.HttpUtility]::UrlEncode("http://www.bing.com") http%3a%2f%2fwww.bing.com
To update the list of namespaces that PowerShell searches by default, specify that namespace in a using
statement:
PS > using namespace System.Web PS > [HttpUtility]::UrlEncode("http://www.bing.com")
Interacting with COM Objects
PowerShell lets you access methods and properties on COM objects the same way you would interact with objects from the .NET Framework. To interact with a COM object, use its ProgId
with the -ComObject
parameter (often shortened to
-Com
) on New-Object
:
PS > $shell = New-Object -Com Shell.Application PS > $shell.Windows() | Select-Object LocationName,LocationUrl
For more information about the COM objects most useful to system administrators, see Chapter 8.
Extending Types
PowerShell supports two ways to add your own methods and properties to any type: the Add-Member
cmdlet and a custom types extension file.
The Add-Member cmdlet
The Add-Member
cmdlet lets you dynamically add methods, properties, and more to an object. It supports the extensions shown in Table 1-12.
Custom type extension files
While the Add-Member
cmdlet lets you customize individual objects, PowerShell also supports configuration files that let you customize all objects of a given type. For example, you might want to add a Reverse()
method to all strings or a
HelpUrl
property (based on the documentation URLs) to all types.
PowerShell adds several type extensions to the file types.ps1xml, in the PowerShell installation directory. This file is useful as a source of examples, but you should not modify it directly. Instead, create a new one and use the Update-TypeData
cmdlet to load your customizations. The following command loads Types.custom.ps1xml from the same directory as your profile:
$typesFile
=
Join-Path
(
Split-Path
$profile
)
"Types.Custom.Ps1Xml"
Update-TypeData
-PrependPath
$typesFile
Writing Scripts, Reusing Functionality
When you want to start packaging and reusing your commands, the best place to put them is in scripts, functions, and script blocks. A script is a text file that contains a sequence of PowerShell commands. A function is also a sequence of PowerShell commands, but is usually placed within a script to break it into smaller, more easily understood segments. A script block is a function with no name. All three support the same functionality, except for how you define them.
Writing Commands
Writing functions
Functions let you package blocks of closely related commands into a single unit that you can access by name:
function SCOPE:name(parameters)
{statement block
}
or:
filter SCOPE:name(parameters)
{statement block
}
Valid scope names are global
(to create a function available to the entire shell), script
(to create a function available only to the current script), local
(to create a function available only to the current scope and subscopes), and private
(to create a function available only to the current scope). The default scope is the local
scope, which follows the same rules as those of default variable scopes.
The content of a function’s statement block follows the same rules as the content of a script. Functions support the $args
array, formal parameters, the $input
enumerator, cmdlet keywords, pipeline output, and equivalent return semantics.
Note
A common mistake is to call a function as you would call a method:
$result
=
GetMyResults
(
$item1
,
$item2
)
PowerShell treats functions as it treats scripts and other commands, so this should instead be:
$result
=
GetMyResults
$item1
$item2
The first command passes an array that contains the items $item1
and $item2
to the GetMyResults
function.
A filter is simply a function where the statements are treated as though they are contained within a process
statement block. For more information about process
statement blocks, see “Cmdlet keywords in commands”.
Note
Commands in your script can access only functions that have already been defined. This can often make large scripts difficult to understand when the beginning of the script is composed entirely of helper functions. Structuring a script in the following manner often makes it more clear:
function
Main
{
(...)
HelperFunction
(...)
}
function
HelperFunction
{
(...)
}
.
Main
Writing script blocks
$objectReference =
{
statement block
}
PowerShell supports script blocks, which act exactly like unnamed functions and scripts. Like both scripts and functions, the content of a script block’s statement block follows the same rules as the content of a function or script. Script blocks support the $args
array, formal parameters, the $input
enumerator, cmdlet keywords, pipeline output, and equivalent return semantics.
As with both scripts and functions, you can either invoke or dot-source a script block. Since a script block does not have a name, you either invoke it directly (&
{ "Hello"
}) or invoke the variable (&
$objectReference
) that contains it.
Running Commands
There are two ways to execute a command (script, function, or script block): by invoking it or by dot-sourcing it.
Invoking
Invoking a command runs the commands inside it. Unless explicitly defined with the GLOBAL
scope keyword, variables and functions defined in the script do not persist once the script exits.
Note
By default, a security feature in PowerShell called
the Execution Policy prevents scripts from running. When you want to enable scripting in PowerShell, you must change this setting. To understand the different execution policies available to you, type Get-Help about_signing
. After selecting an execution policy, use the Set-ExecutionPolicy
cmdlet to configure it:
Set-ExecutionPolicy
RemoteSigned
If the command name has no spaces, simply type its name:
c:\temp\Invoke-Commands.ps1parameter1 parameter2 ...
Invoke-MyFunctionparameter1 parameter2 ...
To run the command as a background job, use the background operator (&
):
c:\temp\Invoke-Commands.ps1 parameter1 parameter2 ...
&
You can use either a fully qualified path or a path relative to the current location. If the script is in the current directory, you must explicitly say so:
.\Invoke-Commands.ps1 parameter1 parameter2 ...
If the command’s name has a space (or the command has no name, in the case of a script block), you invoke the command by using the invoke/call operator (&
) with the command name as the parameter:
& "C:\My Scripts\Invoke-Commands.ps1" parameter1 parameter2 ...
Script blocks have no name, so you place the variable holding them after the invocation operator:
$scriptBlock = { "Hello World" }
& $scriptBlock parameter1 parameter2 ...
If you want to invoke the command within the context of a module, provide a reference to that module as part of the invocation:
$module = Get-Module PowerShellCookbook &$module
Invoke-MyFunctionparameter1 parameter2 ...
&$module
$scriptBlockparameter1 parameter2 ...
Dot-sourcing
Dot-sourcing a command runs the commands inside it. Unlike simply invoking a command, variables and functions defined in the script do persist after the script exits.
You invoke a script by using the dot operator (.
) and providing the command name as the parameter:
. "C:\Script Directory\Invoke-Commands.ps1"Parameters
. Invoke-MyFunctionparameters
. $scriptBlockparameters
When dot-sourcing a script, you can use either a fully qualified path or a path relative to the current location. If the script is in the current directory, you must explicitly say so:
. .\Invoke-Commands.ps1 Parameters
If you want to dot-source the command within the context of a module, provide a reference to that module as part of the invocation:
$module = Get-Module PowerShellCookbook .$module
Invoke-MyFunctionparameters
.$module
$scriptBlockparameters
Parameters
Commands that require or support user input do so through parameters. You can use the Get-Command
cmdlet to see the parameters supported by a command:
PS > Get-Command Stop-Process -Syntax Stop-Process [-Id] <int[]> [-PassThru] [-Force] [-WhatIf] [...] Stop-Process -Name <string[]> [-PassThru] [-Force] [-WhatIf] [...] Stop-Process [-InputObject] <Process[]> [-PassThru] [-Force] [...]
In this case, the supported parameters of the Stop-Process
command are Id
, Name
, InputObject
, PassThru
, Force
, WhatIf
, and Confirm
.
To supply a value for a parameter, use a dash character, followed by the parameter name, followed by a space, and then the parameter value:
Stop-Process
-Id
1234
If the parameter value contains spaces, surround it with quotes:
Stop-Process
-Name
"Process With Spaces"
If a variable contains a value that you want to use for a parameter, supply that through PowerShell’s regular variable reference syntax:
$name
=
"Process With Spaces"
Stop-Process
-Name
$name
If you want to use other PowerShell language elements as a parameter value, surround the value with parentheses:
Get-Process
-Name
(
"Power"
+
"Shell"
)
You only need to supply enough of the parameter name to disambiguate it from the rest of the parameters:
Stop-Process
-N
"Process With Spaces"
If a command’s syntax shows the parameter name in square brackets (such as [-Id]
), then it is positional and you may omit the parameter name and supply only the value. PowerShell supplies these unnamed values to parameters in the order of their position:
Stop-Process
1234
Rather than explicitly providing parameter names and values, you can provide a hashtable that defines them and use the splatting operator:
$parameters
=
@{
Path
=
"c:\temp"
Recurse
=
$true
}
Get-ChildItem
@parameters
To define the default value to be used for the parameter of a command (if the parameter value is not specified directly), assign a value to the PSDefaultParameterValues
hashtable. The keys of this hashtable are command names and parameter names, separated by a colon. Either (or both) may use wildcards. The values of this hashtable are either simple parameter values, or script blocks that will be evaluated dynamically:
PS > $PSDefaultParameterValues["Get-Process:ID"] = $pid PS > Get-Process PS > $PSDefaultParameterValues["Get-Service:Name"] = { Get-Service -Name * | ForEach-Object Name | Get-Random } PS > Get-Service
Providing Input to Commands
PowerShell offers several options for processing input to a command.
Formal parameters
To define a command with simple parameter support:
param([TypeName] $VariableName
=Default
, ... )
To define one with support for advanced functionality:
[CmdletBinding(cmdlet behavior customizations
)] param( [Parameter(Mandatory = $true, Position = 1, ...
)] [Alias("MyParameterAlias
"] [...][TypeName] $VariableName
=Default
, ... )
Formal parameters let you benefit from some of the many benefits of PowerShell’s consistent command-line parsing engine.
PowerShell exposes your parameter names (for example,
$VariableName
) the same way that it exposes parameters in cmdlets. Users need to type only enough of your parameter name to disambiguate it from the rest of the parameters.
If you define a command with simple parameter support, PowerShell attempts to assign the input to your parameters by their position if the user does not type parameter names.
When you add the [CmdletBinding()]
attribute, [Parameter()]
attribute, or any of the validation attributes, PowerShell adds support for advanced parameter validation.
Command behavior customizations
The elements of the [CmdletBinding()]
attribute describe how your script or function interacts with the system:
SupportsShouldProcess = $true
-
If
$true
, enables the-WhatIf
and-Confirm
parameters, which tells the user that your command modifies the system and can be run in one of these experimental modes. When specified, you must also call the$psCmdlet.ShouldProcess()
method before modifying system state. When not specified, the default is$false
. DefaultParameterSetName =
name
-
Defines the default parameter set name of this command. This is used to resolve ambiguities when parameters declare multiple sets of parameters and the user input doesn’t supply enough information to pick between available parameter sets. When not specified, the command has no default parameter set name.
ConfirmImpact =
"High
"-
Defines this command as one that should have its confirmation messages (generated by the
$psCmdlet.ShouldProcess()
method) shown by default. More specifically, PowerShell defines three confirmation impacts:Low
,Medium
, andHigh
. PowerShell generates the cmdlet’s confirmation messages automatically whenever the cmdlet’s impact level is greater than the preference variable. When not specified, the command’s impact isMedium
.
Parameter attribute customizations
The elements of the [Parameter()]
attribute mainly define how your parameter behaves in relation to other parameters (all elements are optional):
Mandatory = $true
-
Defines the parameter as mandatory. If the user doesn’t supply a value to this parameter, PowerShell automatically prompts him for it. When not specified, the parameter is optional.
Position =
position
-
Defines the position of this parameter. This applies when the user provides parameter values without specifying the parameter they apply to (e.g.,
Argument2
inInvoke-MyFunction -Param1 Argument1 Argument2
). PowerShell supplies these values to parameters that have defined aPosition
, from lowest to highest. When not specified, the name of this parameter must be supplied by the user. ParameterSetName =
name
-
Defines this parameter as a member of a set of other related parameters. Parameter behavior for this parameter is then specific to this related set of parameters, and the parameter exists only in the parameter sets that it is defined in. This feature is used, for example, when the user may supply only a Name or ID. To include a parameter in two or more specific parameter sets, use two or more
[Parameter()]
attributes. When not specified, this parameter is a member of all parameter sets. ValueFromPipeline = $true
-
Declares this parameter as one that directly accepts pipeline input. If the user pipes data into your script or function, PowerShell assigns this input to your parameter in your command’s
process {}
block. When not specified, this parameter does not accept pipeline input directly. ValueFromPipelineByPropertyName = $true
-
Declares this parameter as one that accepts pipeline input if a property of an incoming object matches its name. If this is true, PowerShell assigns the value of that property to your parameter in your command’s
process {}
block. When not specified, this parameter does not accept pipeline input by property name. ValueFromRemainingArguments = $true
-
Declares this parameter as one that accepts all remaining input that has not otherwise been assigned to positional or named parameters. Only one parameter can have this element. If no parameter declares support for this capability, PowerShell generates an error for arguments that cannot be assigned.
Parameter validation attributes
In addition to the [Parameter()]
attribute, PowerShell lets you apply other attributes that add behavior or validation constraints to your parameters (all validation attributes are optional):
[Alias("
name
")]
-
Defines an alternate name for this parameter. This is especially helpful for long parameter names that are descriptive but have a more common colloquial term. When not specified, the parameter can be referred to only by the name you originally declared.
[AllowNull()]
-
Allows this parameter to receive
$null
as its value. This is required only for mandatory parameters. When not specified, mandatory parameters cannot receive$null
as their value, although optional parameters can. [AllowEmptyString()]
-
Allows this string parameter to receive an empty string as its value. This is required only for mandatory parameters. When not specified, mandatory string parameters cannot receive an empty string as their value, although optional string parameters can. You can apply this to parameters that are not strings, but it has no impact.
[AllowEmptyCollection()]
-
Allows this collection parameter to receive an empty collection as its value. This is required only for mandatory parameters. When not specified, mandatory collection parameters cannot receive an empty collection as their value, although optional collection parameters can. You can apply this to parameters that are not collections, but it has no impact.
[ValidateCount(
lower limit
,upper limit
)]
-
Restricts the number of elements that can be in a collection supplied to this parameter. When not specified, mandatory parameters have a lower limit of one element. Optional parameters have no restrictions. You can apply this to parameters that are not collections, but it has no impact.
[ValidateLength(
lower limit
,upper limit
)]
-
Restricts the length of strings that this parameter can accept. When not specified, mandatory parameters have a lower limit of one character. Optional parameters have no restrictions. You can apply this to parameters that are not strings, but it has no impact.
[ValidatePattern("
regular expression
")]
-
Enforces a pattern that input to this string parameter must match. When not specified, string inputs have no pattern requirements. You can apply this to parameters that are not strings, but it has no impact.
[ValidateRange(
lower limit
,upper limit
)]
-
Restricts the upper and lower limit of numerical arguments that this parameter can accept. When not specified, parameters have no range limit. You can apply this to parameters that are not numbers, but it has no impact.
[ValidateScript( {
script block
} )]
-
Ensures that input supplied to this parameter satisfies the condition that you supply in the script block. PowerShell assigns the proposed input to the
$_
(or$PSItem
) variable, and then invokes your script block. If the script block returns$true
(or anything that can be converted to$true
, such as nonempty strings), PowerShell considers the validation to have been successful. [ValidateSet("
First Option
", "
Second Option
",
…, "
Last Option
")]
-
Ensures that input supplied to this parameter is equal to one of the options in the set. PowerShell uses its standard meaning of equality during this comparison: the same rules used by the
-eq
operator. If your validation requires nonstandard rules (such as case-sensitive comparison of strings), you can instead write the validation in the body of the script or function. [ValidateNotNull()]
-
Ensures that input supplied to this parameter is not null. This is the default behavior of mandatory parameters, so this is useful only for optional parameters. When applied to string parameters, a
$null
parameter value gets instead converted to an empty string. [ValidateNotNullOrEmpty()]
-
Ensures that input supplied to this parameter is not null or empty. This is the default behavior of mandatory parameters, so this is useful only for optional parameters. When applied to string parameters, the input must be a string with a length greater than one. When applied to collection parameters, the collection must have at least one element. When applied to other types of parameters, this attribute is equivalent to the
[ValidateNotNull()]
attribute.
Pipeline input
To access the data being passed to your command via the pipeline, use the input enumerator that PowerShell places in the $input
special variable:
foreach
(
$element
in
$input
)
{
"Input was: $element"
}
The $input
variable is a .NET enumerator over the pipeline input. Enumerators support streaming scenarios very efficiently but do not let you access arbitrary elements as you would with an array. If you want to process their elements again, you must call the Reset()
method on the $input
enumerator once you reach the end.
If you need to access the pipeline input in an unstructured way, use the following command to convert the input enumerator to an array:
$inputArray
=
@(
$input
)
Cmdlet keywords in commands
When pipeline input is a core scenario of your command, you can include statement blocks labeled begin
, process
, and end
:
param(...) begin { ... } process { ... } end { ... }
PowerShell executes the begin
statement when it loads your command, the process
statement for each item passed down the pipeline, and the end
statement after all pipeline input has been processed. In the process
statement block, the $_
(or
$PSItem
) variable represents the current pipeline object.
When you write a command that includes these keywords, all the commands in your script must be contained within the statement blocks.
Retrieving Output from Commands
PowerShell provides three primary ways to retrieve output from a command.
Exit statement
exit errorlevel
The exit
statement returns an error code from the current command or instance of PowerShell. If called anywhere in a script (inline, in a function, or in a script block), it exits the script. If called outside of a script (for example, a function), it exits PowerShell. The exit
statement sets the $LastExitCode
automatic variable to errorLevel
. In turn, that sets the $?
automatic variable to $false
if errorLevel
is not zero.
Note
Type Get-Help about_automatic_variables
for more information about automatic variables.
Managing Errors
PowerShell supports two classes of errors: nonterminating and terminating. It collects both types of errors as a list in the $error
automatic variable.
Nonterminating Errors
Most errors are nonterminating errors, in that they do not halt execution of the current cmdlet, script, function, or pipeline. When a command outputs an error (via PowerShell’s error-output facilities), PowerShell writes that error to a stream called the error output stream.
You can output a nonterminating error using the Write-Error
cmdlet (or the WriteError()
API when writing a cmdlet).
The $ErrorActionPreference
automatic variable lets you control how PowerShell handles nonterminating errors. It supports the following values, shown in Table 1-13.
Value | Meaning |
---|---|
|
Do not display errors, and do not add them to the |
|
Do not display errors, but add them to the |
|
Treat nonterminating errors as terminating errors. |
|
Display errors, but continue execution of the current cmdlet, script, function, or pipeline. This is the default. |
|
Display a prompt that asks how PowerShell should treat this error. |
Most cmdlets let you configure this explicitly by passing one of these values to the ErrorAction
parameter.
Terminating Errors
A terminating error halts execution of the current cmdlet, script, function, or pipeline. If a command (such as a cmdlet or .NET method call) generates a structured exception (for example, if you provide a method with parameters outside their valid range), PowerShell exposes this as a terminating error. PowerShell also generates a terminating error if it fails to parse an element of your script, function, or pipeline.
You can generate a terminating error in your script using the throw
keyword:
throw message
Note
In your own scripts and cmdlets, generate terminating errors only when the fundamental intent of the operation is impossible to accomplish. For example, failing to execute a command on a remote server should be considered a nonterminating error, whereas failing to connect to the remote server altogether should be considered a terminating error.
You can intercept terminating errors through the try
, catch
, and finally
statements, as supported by many other programming languages:
try {statement block
} catch[exception type]
{error handling block
} catch[alternate exception type]
{alternate error handling block
} finally {cleanup block
}
After a try
statement, you must provide a catch
statement, a finally
statement, or both. If you specify an exception type (which is optional), you may specify more than one catch
statement to handle exceptions of different types. If you specify an exception type, the catch
block applies only to terminating errors of that type.
PowerShell also lets you intercept terminating errors if you define a trap
statement before PowerShell encounters that error:
trap[exception type]
{statement block
[continue or break]
}
If you specify an exception type, the trap
statement applies only to terminating errors of that type.
Within a catch block or trap statement, the $_
(or $PSItem
) variable represents the current exception or error being
processed.
If specified, the continue
keyword tells PowerShell to continue processing your script, function, or pipeline after the point at which it encountered the terminating error.
If specified, the break
keyword tells PowerShell to halt processing the rest of your script, function, or pipeline after the point at which it encountered the terminating error. The default mode is break
, and it applies if you specify neither break
nor continue
.
Formatting Output
Pipeline
|Formatting Command
When objects reach the end of the output pipeline, PowerShell converts them to text to make them suitable for human consumption. PowerShell supports several options to help you control this formatting process, as listed in Table 1-14.
Custom Formatting Files
All the formatting defaults in PowerShell (for example, when you do not specify a formatting command, or when you do not specify formatting properties) are driven by the *.Format.Ps1Xml files in the installation directory.
To create your own formatting customizations, use these files as a source of examples, but do not modify them directly. Instead, create a new file and use the Update-FormatData
cmdlet to load your customizations. The Update-FormatData
cmdlet applies your changes to the current instance of PowerShell. If you wish to load them every time you launch PowerShell, call Update-FormatData
in your profile script. The following command loads Format.custom.ps1xml from the same directory as your profile:
$formatFile
=
Join-Path
(
Split-Path
$profile
)
"Format.Custom.Ps1Xml"
Update-FormatData
-PrependPath
$formatFile
Capturing Output
There are several ways to capture the output of commands in PowerShell, as listed in Table 1-15.
Command | Result |
---|---|
|
Stores the objects produced by the PowerShell command into |
|
Stores the visual representation of the PowerShell command into |
|
Stores the (string) output of the native command into |
|
For most commands, stores the objects produced by the PowerShell command into |
|
Redirects the visual representation of the PowerShell (or standard output of a native command) into |
|
Redirects the visual representation of the PowerShell (or standard output of a native command) into |
|
Redirects the errors from the PowerShell or native command into |
|
Redirects stream number |
|
Redirects the errors from the PowerShell or native command into |
|
Redirects stream number |
|
Redirects both the error and standard output streams of the PowerShell or native command into |
|
Redirects both the error and standard output streams of the PowerShell or native command into |
While output from the Write-Host
cmdlet normally goes directly to the screen, you can use the structured information stream to capture it into a variable:
PS > function HostWriter { Write-Host "Console Output" } PS > $a = HostWriter Console Output PS > $a PS > $a = HostWriter 6>&1 PS > $a Console Output
Common Customization Points
As useful as it is out of the box, PowerShell offers several avenues for customization and personalization.
Console Settings
The Windows PowerShell user interface offers several features to make your shell experience more efficient.
Adjust your font size
Both the Windows Terminal application and the default Windows Console let you adjust your font size.
To temporarily change your font size, hold down the Ctrl key and use the mouse to scroll up or down. In the Windows Terminal application, you can also use the Ctrl+Plus or Ctrl+Minus hotkeys. In the Windows Terminal application, Ctrl+0 resets the font size back to your default.
To change your font size default in the default Windows Console, open the System menu (right-click the title bar at the top left of the console window), select Properties→Font. If you launch Windows PowerShell from the Start menu, it launches with some default modifications to the font and window size. To change your font size default in the Windows Terminal application, add a fontSize
setting to any of your terminal
profiles:
{ "guid": "...", "name": "PowerShell (Demos)", "fontSize": 18, "colorScheme": "Campbell Powershell", "source": "Windows.Terminal.PowershellCore" },
Adjust other Windows Terminal settings
The Windows Terminal application includes a wealth of configuration settings. A sample of these include:
-
Configuring the list of available shells and applications (such as
bash.exe
) -
Color schemes and user interface themes
-
Binding actions to hotkeys
-
Text selection behavior
-
Window transparency
-
Background images
For a full list of these, see the documentation for global settings and general profile settings in Windows Terminal.
Use hotkeys to operate the shell more efficiently
The PowerShell console supports many hotkeys that help make operating the console more efficient, as shown in Table 1-16.
Profiles
PowerShell automatically runs the four scripts listed in Table 1-17 during startup. Each, if present, lets you customize your execution environment. PowerShell runs anything you place in these files as though you had entered it manually at the command line.
Profile purpose | Profile location |
---|---|
Customization of all PowerShell sessions, including PowerShell hosting applications for all users on the system |
InstallationDirectory\profile.ps1 |
Customization of pwsh.exe sessions for all users on the system |
InstallationDirectory\Microsoft.PowerShell_profile.ps1 |
Customization of all PowerShell sessions, including PowerShell hosting applications |
<My Documents>\PowerShell\profile.ps1 |
Typical customization of pwsh.exe sessions |
<My Documents>\PowerShell\Microsoft.PowerShell_profile.ps1 |
In Windows PowerShell, some of these locations will be different.
PowerShell makes editing your profile script simple by defining the automatic variable $profile
. By itself, it points to the “current user, pwsh.exe” profile. In addition, the $profile
variable defines additional properties that point to the other profile locations:
PS > $profile | Format-List -Force AllUsersAllHosts : C:\...Microsoft.PowerShell..\profile.ps1 AllUsersCurrentHost : C:\...\Microsoft.PowerShell_profile.ps1 CurrentUserAllHosts : D:\Lee\PowerShell\profile.ps1 CurrentUserCurrentHost : D:\...\Microsoft.PowerShell_profile.ps1
To create a new profile, type:
New-Item
-Type
file
-Force
$profile
To edit this profile, type:
notepad
$profile
Tab Completion
You can define a TabExpansion2
function to customize the way that PowerShell completes properties, variables, parameters, and files when you press the Tab key.
Your TabExpansion
function overrides the one that PowerShell defines by default, though, so you may want to use its definition as a starting point:
Get-Content
function
:
\
TabExpansion2
User Input
You can define a PSConsoleHostReadLine
function to customize the way that the PowerShell console host (not the Integrated Scripting Environment [ISE]) reads input from the user. This function is responsible for handling all of the user’s keypresses, and finally returning the command that PowerShell should invoke.
Command Resolution
You can intercept PowerShell’s command resolution behavior in three places by assigning a script block to one or all of the PreCommandLookupAction
, PostCommandLookupAction
, or CommandNotFoundAction
properties of $executionContext.SessionState.InvokeCommand
.
PowerShell invokes the PreCommandLookupAction
after the user types a command name, but before it has tried to resolve the command. It invokes the PostCommandLookupAction
once it has resolved a command, but before it executes the command. It invokes the CommandNotFoundAction
when a command is not found, but before it generates an error message. Each script block receives two arguments—CommandName
and CommandLookupEventArgs
:
$executionContext
.
SessionState
.
InvokeCommand
.
CommandNotFoundAction
=
{
param
(
$CommandName
,
$CommandLookupEventArgs
)
(...)
}
If your script block assigns a script block to the CommandScriptBlock
property of the CommandLookupEventArgs
or assigns a
CommandInfo
to the Command
property of the CommandLookupEventArgs
, PowerShell will use that script block or command, respectively. If your script block sets the StopSearch
property to true
, PowerShell will do no further command resolution.
Get PowerShell Pocket Reference, 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.