In powershell is there a difference between having param in a function or putting parameters in the function?

Viewed 805

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" 
}
3 Answers

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

From About Functions - Functions with Parameters - Named Parameters:

You can define any number of named parameters. You can include a default value for named parameters, as described later in this topic.

You can define parameters inside the braces using the Param keyword, as shown in the following sample syntax:

function <name> {
  param ([type]$parameter1[,[type]$parameter2])
  <statement list>
}

You can also define parameters outside the braces without the Param keyword, as shown in the following sample syntax:

function <name> [([type]$parameter1[,[type]$parameter2])] {
  <statement list>
}

While the first method is preferred, there is no difference between these two methods.

Edit

  1. @mklement0 thanks for helpful explication of the latter (emphasized) statement.
  2. This statement (there is no difference between these two methods) is valid despite of 7cc's improper guesswork.
  3. 7cc's answer is right, explained in mklement0's comments below and his updated answer.

In About Functions Advanced Parameters => Attributes of parameters, there are some allusions to the relation of CmdletBinding and Parameter attributes and advanced functions (advanced functions use the CmdletBinding attribute to identify them as functions that act similar to cmdlets):

… if you omit the CmdletBinding attribute, then to be recognized as an advanced function, the function must include the Parameter attribute…

… to be recognized as an advanced function, rather than a simple function, a function must have either the CmdletBinding attribute or the Parameter attribute, or both.

I can't comprehend PowerShell inventors' motivation for such (confusing for me) design…

Related