13
votes

I'm writing a PowerShell script that's a wrapper to an .exe. I want to have some optional script params, and pass the rest directly to the exe. Here's a test script:

param (
    [Parameter(Mandatory=$False)] [string] $a = "DefaultA"
   ,[parameter(ValueFromRemainingArguments=$true)][string[]]$ExeParams   # must be string[] - otherwise .exe invocation will quote
)

Write-Output ("a=" + ($a) + "  ExeParams:") $ExeParams

If I run with the a named param, everything is great:

C:\ > powershell /command \temp\a.ps1 -a A This-should-go-to-exeparams This-also
a=A  ExeParams:
This-should-go-to-exeparams
This-also

However, if I try to omit my param, the first unnamed param is assigned to it:

C:\ > powershell /command \temp\a.ps1 This-should-go-to-exeparams This-also
a=This-should-go-to-exeparams  ExeParams:
This-also

I would expect:

a=DefaultA ExeParams:
This-should-go-to-exeparams
This-also

I tried adding Position=0 to the param, but that produces the same result.

Is there a way to achieve this?
Maybe a different parameter scheme?

2

2 Answers

8
votes

By default, all function parameters are positional. Windows PowerShell assigns position numbers to parameters in the order in which the parameters are declared in the function. To disable this feature, set the value of the PositionalBinding argument of the CmdletBinding attribute to $False.

have a look at How to disable positional parameter binding in PowerShell

function Test-PositionalBinding
{
    [CmdletBinding(PositionalBinding=$false)]
    param(
       $param1,$param2
    )

    Write-Host param1 is: $param1
    Write-Host param2 is: $param2
}
3
votes

The main answer still works in version 5 (according to comments, it may have been broken for a while in version 2).

There is another option: add Position to the ValueFromRemainingArgs parameter.

Sample CommandWrapper.ps1:

param(
    $namedOptional = "default",
    [Parameter(ValueFromRemainingArguments = $true, Position=1)]
    $cmdArgs
)

write-host "namedOptional: $namedOptional"
& cmd /c echo cmdArgs: @cmdArgs

Sample output:

>commandwrapper hello world
namedOptional: default
cmdArgs: hello world

This appears to follow from PowerShell assigning parameter positions from the first parameter with a Position designated.