Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell non-positional, optional params

I'm trying to create a powershell (2.0) script that will accept arguments that follow this basic pattern:

.\{script name} [options] PATH

Where options are any number of optional parameters - think along the lines of '-v' for verbose. The PATH argument will simply be whatever argument is passed in last, and is mandatory. One could call the script with no options and only one argument, and that argument would be assumed to be the Path. I'm having trouble setting up a parameters list that contains only optional parameters but is also non-positional.

This quick script demonstrates the problem I am having:

#param test script
Param(
    $firstArg,
    $secondArg,
    [switch]$thirdArg,
    [Parameter(ValueFromRemainingArguments = $true)]
    $remainingArgs)

write-host "first arg is $firstArg"
write-host "second arg is $secondArg"
write-host "third arg is $thirdArg"
write-host "remaining: $remainingArgs"

When called like so:

.\param-test.ps1 firstValue secondValue

The script outputs:

first arg is firstValue
second arg is secondValue
third arg is False
remaining:

The behavior I am trying to create would have both arguments fall through the optional params and end up in the remainingArgs variable.

This question/answer helpfully provided a way to achieve the desired behavior, but it only seems to work if there is at least one mandatory parameter, and only if it comes before all of the other arguments.

I can demonstrate this behavior by making firstArg mandatory and specifying a position of 0:

#param test script
Param(
    [Parameter(Mandatory=$true, Position = 0)]
    $firstArg,
    $secondArg,
    [switch]$thirdArg,
    [Parameter(ValueFromRemainingArguments = $true)]
    $remainingArgs)

    write-host "first arg is $firstArg"
    write-host "second arg is $secondArg"
    write-host "third arg is $thirdArg"
    write-host "remaining: $remainingArgs"

Run with the same input as before:

.\param-test.ps1 firstValue secondValue

The output is as follows:

first arg is firstValue
second arg is
third arg is False
remaining: secondValue

The first, mandatory argument is assigned, and everything left falls all the way through.

The question is this: How can I set up a params list such that all of the params are optional, and none of them is positional?

like image 716
Fopedush Avatar asked Sep 11 '12 18:09

Fopedush


People also ask

What is the difference between named and positional parameters PowerShell?

A named parameter requires that you type the parameter name and argument when calling the cmdlet. A positional parameter requires only that you type the arguments in relative order.

What is param () in PowerShell?

The PowerShell parameter is a fundamental component of any script. A parameter is a way that developers enable script users to provide input at runtime. If a PowerShell script's behavior needs to change in some way, a parameter provides an opportunity to do so without changing the underlying code.

How do you declare the parameter $value as required parameter in PowerShell?

To make a parameter mandatory add a "Mandatory=$true" to the parameter description. To make a parameter optional just leave the "Mandatory" statement out. Make sure the "param" statement is the first one (except for comments and blank lines) in either the script or the function.


1 Answers

How about this?

function test
{
   param(
      [string] $One,

      [string] $Two,

      [Parameter(Mandatory = $true, Position = 0)]
      [string] $Three
   )

   "One = [$one]  Two = [$two]  Three = [$three]"
}

One and Two are optional, and may only be specified by name. Three is mandatory, and may be provided without a name.

These work:

test 'foo'
    One = []  Two = []  Three = [foo]
test -One 'foo' 'bar'
    One = [foo]  Two = []  Three = [bar]
test 'foo' -Two 'bar'
    One = []  Two = [bar]  Three = [foo]

This will fail:

test 'foo' 'bar'
test : A positional parameter cannot be found that accepts argument 'bar'.
At line:1 char:1
+ test 'foo' 'bar'
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [test], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,test

This doesn't enforce that your mandatory arg is placed last, or that it's not named. But it allows for the basic usage pattern you want.

It also does not allow for more than one value in $Three. This might be what you want. But, if you want to treat multiple non-named params as being part of $Three, then add the ValueFromRemainingArguments attribute.

function test
{
   param(
      [string] $One,

      [string] $Two,

      [Parameter(Mandatory = $true, Position = 0, ValueFromRemainingArguments = $true)]
      [string] $Three
   )

   "One = [$one]  Two = [$two]  Three = [$three]"
}

Now things like this work:

test -one 'foo' 'bar' 'baz'
  One = [foo]  Two = []  Three = [bar baz]

Or even

test 'foo' -one 'bar' 'baz'
    One = [bar]  Two = []  Three = [foo baz]
like image 63
latkin Avatar answered Sep 18 '22 14:09

latkin