In powershell you can make functions with function name {commands}
and make those functions take arguments with this:
function myFunction {
param($var1, $var2)
}
but you can also accomplish this with
function myFunction($var1, $var2) {}
and they would be the same.
For example, if I made a function func1
be:
function func1 {
param($var1, $var2)
echo "$var1 $var2"
}
I would call it by using func1 1 2
where $var1
would be equal to 1
and $var2
would be equal to 2
.
Input:
PS C:\Users\Neko> func1 1 2
Output:
1 2
However, if I do the same thing but instead I did the other method of passing arguments to functions:
function func2($var1, $var2) {
echo "$var1 $var2"
}
I would also call it the same exact way, calling it by using func2 1 2
where $var1
would be equal to 1
and $var2
would be equal to 2
like the previous function.
Input:
PS C:\Users\Neko> func2 1 2
Output:
1 2
So everything seems the same and constant between the two renditions of the function, so my question is, is there a difference between the two methods of passing arguments to functions or are they both actually the same? Even if it is the most minor of details, or just a parsing difference, I would like to know any differences between the two in functions specifically since param has other uses as well.
UPDATE: The arguments you can do in param
like [parameter(Mandatory=$true, ValueFromPipeline=$true)]
and [String[]]
are not unique to param. You can also accomplish this in the other 'non-param' example by doing:
function func2(
[parameter(Mandatory=$true, ValueFromPipeline=$true, etc)]
[String[]]
$var1, $var2
) {
echo "$var1 $var2"
}
To complement 7cc's helpful answer:
While the two syntax forms are mostly interchangeable when you define a function's parameters, only the param(...)
block syntax works in the following circumstances:
If you want to use a [CmdletBinding()]
attribute and its properties to (explicitly) make your function or script an advanced function or script.[1]
If you're writing a script file(*.ps1
) or script block ({ ... }
): the only way to declare parameters for them is is by placing a param(...)
block at the beginning.
Therefore, you may opt to always use the param(...)
block syntax, for consistency across function and script parameter definitions.
If a [CmdletBinding(...)]
) attribute is used, it must directly precede the param(...)
block.
As for:
I would call it by using
func1(1)(2)
No, you would call it as follows:
func1 1 2
That is, PowerShell functions are called like shell commands: without parentheses, separated by whitespace; while your invocation happens to work too, the use of (...)
around the arguments can change their interpretation:
without the enclosing (...)
the arguments are parsed in argument mode, where, notably, strings needn't be quoted
with the enclosing (...)
, are parsed in expression mode, where strings do need to be quoted.
See this answer for more information.
[1] While you can place a [CmdletBinding(...)]
attribute inside the parentheses with the function Foo (...) { ... }
syntax without provoking an error, doing so is effectively ignored. Separately, in the absence of an (effective) explicit [CmdletBinding(...)]
attribute, with either syntax, if you happen to decorate at least one parameter with a [Parameter()]
attribute, you get the default behaviors of an advanced function (e.g., support for automatic common parameters such as -Verbose
), because using [Parameter()]
implicitly makes a function an advanced one (as if a [CmdletBinding()]
attribute - without explicit property values - were in effect). However, if you need an explicit [CmdletBinding(...)]
attribute, so as to opt into non-default advanced-function behaviors, via property values such as PositionalBinding=$false
or SupportsShouldProcess=$true
, use of a param(...)
block is your only option.
One thing is that the CmdletBinding
attribute requires Param
function Echo-Confirm
{
# Here
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
Param ($val=1)
if ($PSCmdlet.ShouldProcess($val) -eq $true) {
Write-Output "Confirmed $val"
}
}
Edit after this comment
The syntax is fine, but CmdletBinding
has no effect
Function foo (
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
[Parameter()]$val=1
) {
# never confirm
if ($PSCmdlet.ShouldProcess($val) -eq $true) {
Write-Output "always here"
}
else {
Write-Output "never here"
}
}
foo -Confirm
# throws an error
foo: A parameter cannot be found that matches parameter name 'confirm'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With