开发者

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?


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)
    }
}


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


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜