Function
Parameters are wrapped inside a function block with param(...)
function Foo {
param (
$Foo
)
}
2
3
4
5
NOTE
Default type of a parameter is System.Object
.
Implicit Parameter
If no parameter name was set, all value passed in will be captured by $args
, an object[]
.
The following example makes an alias for Neovim to mimic the original Vim cli.
function vim {
nvim --clean -c 'source ~/.vimrc' @args
}
vim ./foo.txt
2
3
4
5
NOTE
$args
is not available when any of ParameterAttribute
and CmdletBinding
is applied on the function.
Positional Parameter
Positional parameters allows passing values with explicit names.
function Foo {
param (
[string] $Foo,
[string] $Bar
)
Write-Output "$Foo $Bar"
}
Foo -Foo foo -Bar bar
Foo foo bar # it's the same
2
3
4
5
6
7
8
9
10
11
Or use a explicit position argument on attribute.
function Foo {
param (
[Parameter(Position = 1)] # [!code highlight]
[string] $Bar
[Parameter(Position = 0)] # [!code highlight]
[string] $Foo,
)
Write-Output "$Foo $Bar"
}
Foo -Foo foo -Bar bar
Foo foo bar # it's the same
2
3
4
5
6
7
8
9
10
11
12
13
PowerShell starts counting the position when there's a value belonging to no explicit parameter name. Assuming -Flag
is a switch, -Foo
has position 0
, the value foo
will be assigned to -Foo
.
Foo -Flag foo
Flags
Defining flags that represents a toggle needs a special type called switch
. switch
has the same nature of bool
, but bool
parameter requires explicit assignment when the function being called. While switch
will remain $false
when unspecified.
function Foo {
param (
[switch]$Foo
[bool]$Bar
)
}
# this is why we should use `switch` instead.
Foo -Foo -Bar $true # [!code highlight]
2
3
4
5
6
7
8
9
Manual assignment is also available:
Foo -f:$false -b $true
Default Parameter
- Explicitly typed parameters can have implicit default value.
switch
andbool
is$false
by default.string
is[string]::Empty
by default.- Numeric types are zero value by default.
- Parameters without type annotation are always typed as
object
which has the default value$null
. - Can override default value by
=
in declaration.
& { param([string]$name) $name -eq [string]::Empty } # True
& { param($name) $name -eq $null } # True
& { param([int]$age) $age -eq 0 } # True
& { param([switch]$is) $is -eq $false } # True
function Foo {
param (
[string]$foo = "foo"
)
}
2
3
4
5
6
7
8
9
10
11
NOTE
For overriding default parameter outside the function, see $PSDefaultParameterValues
Required Parameter
All parameters are optional by default. Use [Parameter(Mandatory=$true)]
to mark it as required.
param (
[Parameter(Mandatory=$true)]
[string]$RequiredName
)
2
3
4
NOTE
You can omit assignment for boolean attribute parameter.
param (
[Parameter(Mandatory)] # Mandatory is true now
[string]$RequiredName
)
2
3
4
Parameter Alias
Parameters can have aliases. It's not needed for most of time though since pwsh can distinguish option by leading string.
function Person {
param (
[Alias('n')] # [!code highlight]
[string]$Name,
[Alias('a', 'yearsold')] # can have multiple aliases!
[int]$Age
)
Write-Host "Name: $Name, Age: $Age"
}
Person -n "Alice" -a 30 # [!code highlight]
2
3
4
5
6
7
8
9
10
11
12
Parameter Validation
Pass a validation logic as script block to ValidateScript
attribute, $_
represents singular value of the parameter or current item of a collection. Will throw an error if any parameter does not satisfies the condition.
param (
[ValidateScript({ ($_ % 2) -ne 0 })]
[int[]]$Odd
[ValidateScript({ $_.Length < 5 }, ErrorMessage = "{0} is not valid")] # 0 is the input value
[string]$Name
)
2
3
4
5
6
ValidateLength(min, max)
ValidateCount(min, max)
AllowNull()
AllowEmptyString()
AllowEmptyCollection()
ValidatePattern(regex)
ValidateRange(min, max)
- or any value of enum
ValidateRangeKind
:Positive
,Negative
,NonNegative
,NonPositive
ps1param( [ValidateRange("Positive")] [int]$Number )
1
2
3
4- or any value of enum
ValidateSet(foo, bar, ...)
: provides completion for predefined entriesValidateNotNull()
ValidateNotNullOr()
ValidateNotNullOrEmpty()
: not a empty string or collection or nullValidateNotNullOrWhiteSpace()
ValidateDrive(drive, ...)
: check whether specified path is valid from certain PSDriveps1param( [ValidateDrive("C", "D")] [string]$Path )
1
2
3
4
Pass By Reference
Parameter passed by reference is implemented by a wrapper System.Management.Automation.PSReference
. Value types are passed by value by default, the pass as reference, mark the parameter with [ref]
. Casting to [ref]
generates a new wrapper containing the value.
function Foo {
param ([ref][int]$foo) # [!code highlight]
$foo.Value = 250
$foo.Value
}
$bar = 1
Foo ([ref]$bar)
$bar # 250
2
3
4
5
6
7
8
9
NOTE
[ref]
can only be marked before type annotation.
Named Blocks
In a simple function where there's only one series of parameters being taken, we don't have to use any complex logic. But things will explode when we're dealing with a pipeline input which might bring multiple objects.
The pipeline mechanism is essentially based on the Enumerator
so if we collect all items into a new collection as parameter value, it can be a huge performance issue. So named blocks are essentially to defined a shared process logic for each object in the pipeline input, and other logic like initializationa and finalization.
begin
: state initializationa for the pipeline iteration.process
: logic for each pipeline iteration.end
: final action for the completed pipeline iteration.clean
: afinally
block to execute clean up no matter what happens.(PowerShell 7.3+)
function Foo {
begin {}
process {}
end {}
clean {}
}
2
3
4
5
6
NOTE
When no named block were specified, end
block is used to represent the whole logic of a simple function.
function Foo {
end {
echo hello
}
}
# equivalent to
function Foo {
echo hello
}
2
3
4
5
6
7
8
9
Filter Function
Filter is a special kind of function that implicitly accepts pipeline to perform transformation(select) or filtering(where). Filter is useful when you need to reuse the same logic for unknown pipeline manipulations, reducing hard coding.
filter Foo {
"$_" # can transform
}
# equivalent to
function Foo {
process {
"$_"
}
}
2
3
4
5
6
7
8
9
10
Mimicking Cmdlet
A function would generally not acting a cmdlet unless it was annotated with CmdletBinding()
attribute.
CmdletBinding()
can have the following properties:
DefaultParameterSetName
: pwsh will prefer this name when there's a ambiguity between syntax provided.HelpURI
: link to documenetationSupportsPaging
: implicitly adds parameters-First
,-Skip
,-IncludeTotalCount
, value accessible by$PSCmdlet.PagingParameters
ps1function foo { [CmdletBinding(SupportsPaging)] param() $PSCmdlet.PagingParameters.Skip $PSCmdlet.PagingParameters.First $PSCmdlet.PagingParameters.IncludeTotalCount }
1
2
3
4
5
6
7SupportsShouldProcess
: implicitly adds-Confirm
and-WhatIf
ConfirmImpact
: specify impact of-Confirm
PositionalBinding
:
Parameter Set
How a same cmdlet manage different syntax for different usages? The trick is Parameter Set. Parameter Set is a classification on paramater to distinguish or limit the use of parameters from scenarios.
- a parameter set must have at least one unique parameter to others to identify the set
- a parameter can be member of multiple parameter sets.
- a parameter can have different roles in different Parameter Set, can be mandatory in one and optional in another
- a parameter without explicit Parameter Set belongs to all other Parameter Set
- at least one parameter in the Parameter Set is mandatory
- only one parameter in set can accept
ValueFromPipeline
Parameter Set Idetifier at Runtime
$PSCmdlet.ParameterSetName
reflects the Parameter Set been chosen when a cmdlet is executing with certain syntax.
Common Parameters
Any function or cmdlet applied with CmdletBinding()
or Parameter()
attribute has the following implicit parameters added by PowerShell:
ErrorAction (ea): specify action on error
ErrorVariable (ev): declare inline and store the error on the variable instead of
$Error
. Use-ErrorVariable +var
to append error to the variableps1gcm foo -ev bar # inline declaration for $bar $bar # contains error gcm baz -ev +bar $bar # contains two errors
1
2
3
4NOTE
The inline variable is an
ArrayList
InformationAction (infa): similar to
ea
InformationVariable (iv): similar to
ev
WarningAction (wa): similar to
ea
WarningVariable (wv): similar to
ev
ProgressAction (proga)
OutVariable (ov): declare inline and store the output to the variable. Similar to
ev
. It's interesting that-OutVariable
collects incremnentally. It collects new item from pipeline on each iteration.ps11..5 | % { $_ } -OutVariable foo | % { "I am $foo" } # I am 1 # I am 1,2 # I am 1,2,3 # I am 1,2,3,4 # I am 1,2,3,4,5
1
2
3
4
5
6Debug (db): print verbose debug message, overrides
$DebugPreference
OutBuffer (ob)
PipelineVariable (pv)
Verbose (vb): whether display the verbose message from
Write-Verbose
Mitigation Parameters
Mitigation parameters were added when CmdletBinding(SupportsShouldProcess)
was applied.
- WhatIf (wi): shows explaination for the command without executing it.
- Confirm (cf): ask for confirmation when executing the command.
Return
PowerShell allows implicit return, and multiple implicit returns.
NOTE
Implicit returns are auto-collected as an array or single value. And it does not print out anything.
function Sum {
param([int]$l, [int]$r)
$l + $r # implicit return
}
# You won't need to declare an array and append it on each loop!
# they're collected automatically as they're implicit returns
function Foo {
for($i = 0; $i -lt 10; $i = $i + 1) {
$i
}
}
(Foo).GetType().Name # object[]
2
3
4
5
6
7
8
9
10
11
12
13
14
Explicit return is surely supported, but more like a necessity to exit inside a flow.
function Sum {
param([int]$l, [int]$r)
return $l + $r # explicit return
$r + $l # not reachable
}
2
3
4
5
Output Type
OutputTypeAttribute(type: System.Type | System.String, parameterSet?: System.String)
matters when you need auto generated help for the function you write. A function can have different return types for different parameter sets. PowerShell never do type checking by this attribute, it's just responsible for help generation, write with carefulness!
[CmdletBinding(DefaultParameterSetName = 'ID')]
[OutputType('System.Int32', ParameterSetName = 'ID')]
[OutputType([string], ParameterSetName = 'Name')]
param (
[Parameter(Mandatory, ParameterSetName = 'ID')]
[int[]]$UserID,
[Parameter(Mandatory, ParameterSetName = 'Name')]
[string[]]$UserName
)
2
3
4
5
6
7
8
9