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".

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 scripts

To write a script, write your PowerShell commands in a text editor and save the file with a .ps1 extension.

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.ps1 parameter1 parameter2 ...
Invoke-MyFunction parameter1 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:\Script Directory\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-MyFunction parameter1 parameter2 ...
& $module $scriptBlock parameter1 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-MyFunction parameters
. $scriptBlock parameters

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-MyFunction parameters
. $module $scriptBlock parameters

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] [-Confirm] [...]
Stop-Process -Name <string[]> [-PassThru] [-Force] [-WhatIf] [-Confirm] [...]
Stop-Process [-InputObject] <Process[]> [-PassThru] [-Force] [-WhatIf] [...]

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.

Argument array

To access the command-line arguments by position, use the argument array that PowerShell places in the $args special variable:

$firstArgument = $args[0]
$secondArgument = $args[1]
$argumentCount = $args.Count

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, and High. 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 is Medium.

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 them 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 in Invoke-MyFunction -Param1 Argument1 Argument2). PowerShell supplies these values to parameters that have defined a Position, 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.

$MyInvocation automatic variable

The $MyInvocation automatic variable contains information about the context under which the script was run, including detailed information about the command ( MyCommand), the script that defines it (ScriptName), and more.

Retrieving Output from Commands

PowerShell provides three primary ways to retrieve output from a command.

Pipeline output

any command

The return value/output of a script is any data that it generates but does not capture. If a command contains:

"Text Output"
5*5

then assigning the output of that command to a variable creates an array with the two values Text Output and 25.

Return statement

return value

The statement:

return $false

is simply a short form for pipeline output:

$false
return

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.