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?
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.
-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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With