Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapper function in PowerShell: Pass remaining parameters

I’m trying to write a wrapper function in PowerShell that basically evaluates the first parameter and based on that runs a program on the computer. All the remaining parameters to the wrapper function should then be passed to the program that is ran as well.

So it should look something like this:

function test ( [string] $option )
{
    if ( $option -eq 'A' )
    {
        Write-Host $args
    }
    elseif ( $option -eq 'B' )
    {
        . 'C:\Program Files\some\program.exe' $args
    }
}

Now just adding $args does not work, so what do I have to do to make it work? Another option would probably be using Invoke-Expression, but it feels a bit like eval so I want to avoid if possible, and in addition I think doing it like that would limit me to string-only parameters right? If possible I would want to have the full support for the wrapped program/cmdlet - basically like a dynamic alias. Is that even possible?

like image 240
poke Avatar asked Sep 14 '11 11:09

poke


3 Answers

  • Your solution works as-is for external programs (such as your C:\Program Files\some\program.exe example): you can always pass an array of values (which is what $args is) to an external program, and its elements will be passed as individual arguments (stringified, if necessary).

  • You can make your solution work with any command if you change $args to @args[1], to take advantage of a PowerShell parameter-passing technique called splatting:

function test ( [string] $option )
{
    if ( $option -eq 'A' )
    {
        Write-Host $args
    }
    elseif ( $option -eq 'B' )
    {
        # Use @args to also support passing *named* arguments
        # through to *PowerShell* commands.
        & $someCommand @args
    }
}

Caveats:

  • The automatic $args variable, which collects all arguments for which no parameter was declared, is only available in simple (non-advanced) functions and scripts; advanced functions and scripts - those that use the [CmdletBinding()] attribute and/or [Parameter()] attributes - require that all potential parameters be declared.

  • PowerShell has built-in magic that makes the automatic array variable $args also support passing named parameters through via splatting, which no custom array or collection supports.

    • By contrast, custom arrays and collections only support splatting positional (unnamed) arguments, which, however, covers all calls to external programs.
    • When calling PowerShell commands, this limitation is problematic, however: For instance, if you wanted to pass the named argument -Path C:\ through to the Set-Location cmdlet via splatting, using a custom collection parameter declared via ValueFromRemaining Arguments, as shown in OldFart's answer (Set-Location @Remaining), would not work; to support passing through named arguments (other than via @args, if available), you must use hashtable-based splatting.

Therefore, if your function is an advanced one and you need to support passing named arguments through to other PowerShell commands, a different approach is required: this answer shows two alternatives.


[1] With external programs, there is a corner case where @args behaves differently from $args, namely if the $args array contains --%, the stop-parsing symbol: @args recognizes it, $args treats it as a literal.

like image 53
mklement0 Avatar answered Oct 17 '22 06:10

mklement0


This sort of does what you ask. You may run into trouble if you need to pass dash-prefixed options to the executable that conflict or cause ambiguity with the PowerShell common parameters. But this may get you started.

function Invoke-MyProgram
{
    [CmdletBinding()]
    Param
    (
        [parameter(mandatory=$true, position=0)][string]$Option,
        [parameter(mandatory=$false, position=1, ValueFromRemainingArguments=$true)]$Remaining
    )

    if ($Option -eq 'A')
    {
        Write-Host $Remaining
    }
    elseif ($Option -eq 'B')
    {
        & 'C:\Program Files\some\program.exe' @Remaining # NOTE: @ not $ (splatting)
    }
}
like image 23
OldFart Avatar answered Oct 17 '22 08:10

OldFart


What you have written does work. Note that what is there is $args is the unnamed arguments that are over and above the parameters expected by the function.

So if you call test as

test -option "A" 1 2 3

$args will have 1,2,3

Note that if you call test as

test -option "A" -other "B" 1 2 3

$args will have -other,B,1,2,3

like image 42
manojlds Avatar answered Oct 17 '22 08:10

manojlds