Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

powershell

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" 
}
like image 669
Nico Nekoru Avatar asked May 21 '20 21:05

Nico Nekoru


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

like image 110
mklement0 Avatar answered Oct 19 '22 22:10

mklement0


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'.
like image 6
7cc Avatar answered Oct 19 '22 23:10

7cc