565

If I have a function which accepts more than one string parameter, the first parameter seems to get all the data assigned to it, and remaining parameters are passed in as empty.

A quick test script:

Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC", "DEF")

The output generated is

$arg1 value: ABC DEF
$arg2 value: 

The correct output should be:

$arg1 value: ABC
$arg2 value: DEF

This seems to be consistent between v1 and v2 on multiple machines, so obviously, I'm doing something wrong. Can anyone point out exactly what?

mklement0
  • 382,024
  • 64
  • 607
  • 775
Nasir
  • 10,935
  • 8
  • 31
  • 39

15 Answers15

736

Parameters in calls to functions in PowerShell (all versions) are space-separated, not comma separated. Also, the parentheses are entirely unneccessary and will cause a parse error in PowerShell 2.0 (or later) if Set-StrictMode -Version 2 or higher is active. Parenthesised arguments are used in .NET methods only.

function foo($a, $b, $c) {
   "a: $a; b: $b; c: $c"
}

ps> foo 1 2 3
a: 1; b: 2; c: 3
mklement0
  • 382,024
  • 64
  • 607
  • 775
x0n
  • 51,312
  • 7
  • 89
  • 111
  • 33
    Most important thing that has finally helped 'stick' this in my mind is the last sentence: "Parenthesised arguments are used in .NET Methods only." – Ashley Dec 12 '13 at 17:25
  • 4
    I prefer using the paranthesis and comma separated.. is it possible to do this in powershell? – sam yi Mar 19 '14 at 04:27
  • 10
    @samyi No. Passing a `(1,2,3)` to a function is effectively treated as an array; a single argument. If you want to use OO method style arguments, use modules: `$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)` – x0n Mar 19 '14 at 13:18
  • 6
    `Powershell` is a shell language and it's common for shell languages to use spaces as a token separator. I wouldn't say `Powershell` is being different here, it's right in line with other system default shells like `cmd`, `sh`, `bash`, etc. – codewario Jun 06 '19 at 21:52
  • this seems to be different for object method calls such as $var.method($var1,$var2). i get a syntax error if i remove the comma. what's going on here? – Shayan Zafar May 07 '21 at 18:41
  • 1
    @ShayanZafar as I said in my original post, that syntax is for .NET framework calls. Only Native powershell functions/cmdlets use spaces. – x0n May 12 '21 at 02:55
  • 1
    This is unfortunate. The latest pwsh can look close to a normal programming language, invoking methods the way we used to would be awesome. – Serj Oct 14 '22 at 18:36
  • @serj - but it's a scripting language - space-separated arguments are the norm for shell languages, as codewario says above. – x0n Oct 20 '22 at 17:16
  • Just trying PowerShell and my mind is tricking me to use Java syntax everywhere!!! If possible I would give 1000 votes to this answer – Alwaysa Learner Apr 23 '23 at 18:30
  • IMHO the fact that this question and answer has so many upvotes goes to show that the current implementation is.... not intuitive. – Urasquirrel May 12 '23 at 20:48
335

The correct answer has already been provided, but this issue seems prevalent enough to warrant some additional details for those wanting to understand the subtleties.

I would have added this just as a comment, but I wanted to include an illustration--I tore this off my quick reference chart on PowerShell functions. This assumes function f's signature is f($a, $b, $c):

Syntax pitfalls of a function call

Thus, one can call a function with space-separated positional parameters or order-independent named parameters. The other pitfalls reveal that you need to be cognizant of commas, parentheses, and white space.

For further reading, see my article Down the Rabbit Hole: A Study in PowerShell Pipelines, Functions, and Parameters. The article contains a link to the quick reference/wall chart as well.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Michael Sorens
  • 35,361
  • 26
  • 116
  • 172
93

There are some good answers here, but I wanted to point out a couple of other things. Function parameters are actually a place where PowerShell shines. For example, you can have either named or positional parameters in advanced functions like so:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=1)]
         [int] $Id
    )
}

Then you could either call it by specifying the parameter name, or you could just use positional parameters, since you explicitly defined them. So either of these would work:

Get-Something -Id 34 -Name "Blah"
Get-Something "Blah" 34

The first example works even though Name is provided second, because we explicitly used the parameter name. The second example works based on position though, so Name would need to be first. When possible, I always try to define positions so both options are available.

PowerShell also has the ability to define parameter sets. It uses this in place of method overloading, and again is quite useful:

function Get-Something
{
    [CmdletBinding(DefaultParameterSetName='Name')]
    Param
    (
         [Parameter(Mandatory=$true, Position=0, ParameterSetName='Name')]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=0, ParameterSetName='Id')]
         [int] $Id
    )
}

Now the function will either take a name, or an id, but not both. You can use them positionally, or by name. Since they are a different type, PowerShell will figure it out. So all of these would work:

Get-Something "some name"
Get-Something 23
Get-Something -Name "some name"
Get-Something -Id 23

You can also assign additional parameters to the various parameter sets. (That was a pretty basic example obviously.) Inside of the function, you can determine which parameter set was used with the $PsCmdlet.ParameterSetName property. For example:

if($PsCmdlet.ParameterSetName -eq "Name")
{
    Write-Host "Doing something with name here"
}

Then, on a related side note, there is also parameter validation in PowerShell. This is one of my favorite PowerShell features, and it makes the code inside your functions very clean. There are numerous validations you can use. A couple of examples are:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [ValidatePattern('^Some.*')]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=1)]
         [ValidateRange(10,100)]
         [int] $Id
    )
}

In the first example, ValidatePattern accepts a regular expression that assures the supplied parameter matches what you're expecting. If it doesn't, an intuitive exception is thrown, telling you exactly what is wrong. So in that example, 'Something' would work fine, but 'Summer' wouldn't pass validation.

ValidateRange ensures that the parameter value is in between the range you expect for an integer. So 10 or 99 would work, but 101 would throw an exception.

Another useful one is ValidateSet, which allows you to explicitly define an array of acceptable values. If something else is entered, an exception will be thrown. There are others as well, but probably the most useful one is ValidateScript. This takes a script block that must evaluate to $true, so the sky is the limit. For example:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [ValidateScript({ Test-Path $_ -PathType 'Leaf' })]
         [ValidateScript({ (Get-Item $_ | select -Expand Extension) -eq ".csv" })]
         [string] $Path
    )
}

In this example, we are assured not only that $Path exists, but that it is a file, (as opposed to a directory) and has a .csv extension. ($_ refers to the parameter, when inside your scriptblock.) You can also pass in much larger, multi-line script blocks if that level is required, or use multiple scriptblocks like I did here. It's extremely useful and makes for nice clean functions and intuitive exceptions.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user2233949
  • 2,053
  • 19
  • 22
  • 3
    +1 for demonstrating `My_Function -NamedParamater "ParamValue"` function call style. This is a pattern that more PS script code should follow for readability. – Mister_Tom Nov 22 '17 at 19:46
  • 1
    Is it a typo to have two `Position=0` ? – Jason Lee Jul 10 '21 at 09:37
  • 1
    No it's not a typo. That would be the case when you're using parameter sets, which are basically just method overloads. So in that case either `name` OR `id` could be passed, but not both. Since both are position 0, PowerShell will figure out which one you're using based on the type, if you don't specify the parameter name. (One is `int` and one is `string`) – user2233949 Jul 12 '21 at 12:56
55

You call PowerShell functions without the parentheses and without using the comma as a separator. Try using:

test "ABC" "DEF"

In PowerShell the comma (,) is an array operator, e.g.

$a = "one", "two", "three"

It sets $a to an array with three values.

codewario
  • 19,553
  • 20
  • 90
  • 159
Todd
  • 5,017
  • 1
  • 25
  • 16
22
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test "ABC" "DEF"
Rob W
  • 341,306
  • 83
  • 791
  • 678
John B
  • 237
  • 2
  • 2
14

If you're a C# / Java / C++ / Ruby / Python / Pick-A-Language-From-This-Century developer and you want to call your function with commas, because that's what you've always done, then you need something like this:

$myModule = New-Module -ascustomobject { 
    function test($arg1, $arg2) { 
        echo "arg1 = $arg1, and arg2 = $arg2"
    }
}

Now call:

$myModule.test("ABC", "DEF")

and you'll see

arg1 = ABC, and arg2 = DEF
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ryan Shillington
  • 23,006
  • 14
  • 93
  • 108
  • 3
    Java, C++, Ruby, and Python are not from this century (only C#), presuming the [Gregorian calendar](https://en.wikipedia.org/wiki/Gregorian_calendar) (though some have evolved more than others). – Peter Mortensen Oct 26 '19 at 09:04
  • Heh. @PeterMortensen your argument is that I should say "Pick a language from either this century or the last one"? :-) – Ryan Shillington Oct 26 '19 at 20:25
14

Because this is a frequent viewed question, I want to mention that a PowerShell function should use approved verbs (Verb-Noun as the function name). The verb part of the name identifies the action that the cmdlet performs. The noun part of the name identifies the entity on which the action is performed. This rule simplifies the usage of your cmdlets for advanced PowerShell users.

Also, you can specify things like whether the parameter is mandatory and the position of the parameter:

function Test-Script
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$arg1,

        [Parameter(Mandatory=$true, Position=1)]
        [string]$arg2
    )

    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

To pass the parameter to the function you can either use the position:

Test-Script "Hello" "World"

Or you specify the parameter name:

Test-Script -arg1 "Hello" -arg2 "World"

You don't use parentheses like you do when you call a function within C#.


I would recommend to always pass the parameter names when using more than one parameter, since this is more readable.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • FYI, approved verbs list link no longer works, but can now be found here - https://learn.microsoft.com/en-us/powershell/developer/cmdlet/approved-verbs-for-windows-powershell-commands – Keith Langmead Sep 21 '18 at 15:23
  • @KeithLangmead Thank you Keith, I updated my answer as well. – Martin Brandl Sep 21 '18 at 18:51
  • 1
    "Verb-Noun" as in both verb and noun capitalised? Perhaps change the answer to be more explicit about it? – Peter Mortensen Oct 26 '19 at 09:13
  • I've never understood the "you should use **approved** verbs" argument. I'm not contesting the `Verb-Noun` nomenclature, but what (possibly obscure as I've never run into a problem) issues would I run into with the example function name `Setup-Node` vs. `Initialize-Node` or `Bootstrap-Node`? – codewario Feb 06 '20 at 17:18
  • 1
    well, consider you expose a `Get-Node` cmdlet. It would be clear for us that we have to invoke `Get-Node`, not `Retrieve-Node`, nor `Receive-Node`, nor ..... – Martin Brandl Feb 06 '20 at 19:12
  • 1
    Also meaningfull to add the `[Alias('something')]` before the Param() section. This allows the use of non-verb-approved functions (like eg. gci, ls, dir, cd ...). Example: `function Test-Script { [CmdletBinding()] [Alias('tst')] Param() Write-Output "This function works."}` – IT M Sep 02 '20 at 06:30
12

If you don't know (or care) how many arguments you will be passing to the function, you could also use a very simple approach like;

Code:

function FunctionName()
{
    Write-Host $args
}

That would print out all arguments. For example:

FunctionName a b c 1 2 3

Output

a b c 1 2 3

I find this particularly useful when creating functions that use external commands that could have many different (and optional) parameters, but relies on said command to provide feedback on syntax errors, etc.

Here is a another real-world example (creating a function to the tracert command, which I hate having to remember the truncated name);

Code:

Function traceroute
{
    Start-Process -FilePath "$env:systemroot\system32\tracert.exe" -ArgumentList $args -NoNewWindow
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Draino
  • 121
  • 1
  • 2
8

If you try:

PS > Test("ABC", "GHI") ("DEF")

you get:

$arg1 value: ABC GHI
$arg2 value: DEF

So you see that the parentheses separates the parameters


If you try:

PS > $var = "C"
PS > Test ("AB" + $var) "DEF"

you get:

$arg1 value: ABC
$arg2 value: DEF

Now you could find some immediate usefulness of the parentheses - a space will not become a separator for the next parameter - instead you have an eval function.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RaSor
  • 869
  • 10
  • 11
  • 4
    Parens don't separate parameters. They define array. – n0rd Oct 12 '13 at 19:16
  • 1
    Parens don't define an array, they define a group, which powershell can interpret as an array. Arrays are defined with an at sign (`@`) before the leading paren, like this empty array: `@()`; or this array with two numbers: `@(1, 2)`. – VertigoRay Mar 11 '19 at 22:14
7

I don't see it mentioned here, but splatting your arguments is a useful alternative and becomes especially useful if you are building out the arguments to a command dynamically (as opposed to using Invoke-Expression). You can splat with arrays for positional arguments and hashtables for named arguments. Here are some examples:

Note: You can use positional splats with external commands arguments with relative ease, but named splats are less useful with external commands. They work, but the program must accept arguments in the -Key:Value format as each parameter relates to the hashtable key/value pairs. One example of such software is the choco command from the Chocolatey package manager for Windows.

Splat With Arrays (Positional Arguments)

Test-Connection with Positional Arguments

Test-Connection www.google.com localhost

With Array Splatting

$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentArray

Note that when splatting, we reference the splatted variable with an @ instead of a $. It is the same when using a Hashtable to splat as well.

Splat With Hashtable (Named Arguments)

Test-Connection with Named Arguments

Test-Connection -ComputerName www.google.com -Source localhost

With Hashtable Splatting

$argumentHash = @{
  ComputerName = 'www.google.com'
  Source = 'localhost'
}
Test-Connection @argumentHash

Splat Positional and Named Arguments Simultaneously

Test-Connection With Both Positional and Named Arguments

Test-Connection www.google.com localhost -Count 1

Splatting Array and Hashtables Together

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
codewario
  • 19,553
  • 20
  • 90
  • 159
  • This did not work for me but I did something like this instead $Results = GetServerInformation $Servers $ServerNames – Mike Q Jul 24 '20 at 18:25
  • Splatting is the syntax used to take a hashtable or array and unroll those as named arguments or positional arguments to another command, as opposed to passing the arguments directly to the command as you have done. If you post a question about your splat not working someone may be able to shed some light on your particular issue. – codewario Jul 24 '20 at 18:30
5

I don't know what you're doing with the function, but have a look at using the 'param' keyword. It's quite a bit more powerful for passing parameters into a function, and makes it more user friendly. Below is a link to an overly complex article from Microsoft about it. It isn't as complicated as the article makes it sound.

Param Usage

Also, here is an example from a question on this site:

Check it out.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • Thanks for the answer. However, I was having issues when calling the function. Didn't matter if the function was declared with param or without it. – Nasir Feb 16 '11 at 14:08
4
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC") ("DEF")
kleopatra
  • 51,061
  • 28
  • 99
  • 211
3

I stated the following earlier:

The common problem is using the singular form $arg, which is incorrect. It should always be plural as $args.

The problem is not that. In fact, $arg can be anything else. The problem was the use of the comma and the parentheses.

I run the following code that worked and the output follows:

Code:

Function Test([string]$var1, [string]$var2)
{
    Write-Host "`$var1 value: $var1"
    Write-Host "`$var2 value: $var2"
}

Test "ABC" "DEF"

Output:

$var1 value: ABC
$var2 value: DEF
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eric
  • 31
  • 1
  • 4
    Thank you my friend, however, you're a couple of years late :-) The top three answers here had sufficiently addressed the issue. May I suggest heading over to Unanswered section and trying some of those questions? – Nasir Nov 08 '13 at 13:49
2
Function Test {
    Param([string]$arg1, [string]$arg2)

    Write-Host $arg1
    Write-Host $arg2
}

This is a proper params declaration.

See about_Functions_Advanced_Parameters.

And it indeed works.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

You can pass parameters in a function like this also:

function FunctionName()
{
    Param ([string]$ParamName);
    # Operations
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kaushal Khamar
  • 2,097
  • 1
  • 17
  • 23
  • 4
    That would be defining parameters for a function, the original question was about how to specify parameters when you call the function. – Nasir May 15 '16 at 22:07