Reputation: 7334
I want to pass all parameters from one powershell script to another script without knowing the named parameters. That other script is out of my control and I can not predict all the parameters it takes.
Example wrapper: wrapper.ps1
Set-StrictMode -v 2
$ErrorActionPreference="Stop"
write-host "ArgsIn:" $Args
$mypath= Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$Cmd, $NewArgs =$Args | foreach { $_ -replace "one",'two' }
$Cmd = $mypath+"\"+$Cmd
write-host "Running:" $Cmd $NewArgs
& $Cmd @NewArgs
exit $LastExitCode
Example called script: test.ps1
)
Param(
[Parameter(Mandatory=$false)] [string]$BAR,
[Parameter(Mandatory=$false)] [string]$FOO
)
write-host "test: Args:" $Args
write-host "FOO" $FOO
write-host "BAR" $BAR
exit 0
If I call test.ps1
it works as expected:
PS C:\Test> .\test.ps1 -foo one -bar three
test: Args:
FOO one
BAR three
But if I try it via the wrapper, it maps the parameters positionally instead.
PS C:\Test> .\wrapper.ps1 test.ps1 -foo one
ArgsIn: test.ps1 -foo one
Running: C:\Test\test.ps1 -foo two
test: Args: test.ps1 -foo one
FOO two
BAR -foo
I have tried various alternative forms to call the script (including invoke-command
and invoke-expression
).
One way that appeared to work was using
invoke-expression "& `"$Cmd`" $NewArgs"
but that breaks as soon as any parameter contains a space:
PS C:\Test> .\wrapper.ps1 test.ps1 -foo "one three"
ArgsIn: test.ps1 -foo one three
Running: C:\Test\test.ps1 -foo two three
test: Args:
FOO two
BAR three
Is there any sensible way to do what I want?
Upvotes: 2
Views: 2127
Reputation: 23613
Excerpt from the About Splatting Syntax:
To provide parameter values for positional parameters, in which parameter names are not required, use the array syntax. To provide parameter name and value pairs, use the hash table syntax. The splatted value can appear anywhere in the parameter list.
In other words, if want to pass named parameters, you should splat your parameters as a hash table (rather than a array). Something like:
[CmdletBinding()] Param (
$Cmd,
[parameter(ValueFromRemainingArguments=$true)]$RemainingArguments
)
Set-StrictMode -v 2
$ErrorActionPreference="Stop"
$Cmd = $PSScriptRoot + '\' + $Cmd
$ArgName = $Null; $NewArgs = @{}
ForEach ($Arg in $RemainingArguments) {
If ($Arg.StartsWith('-')) {$ArgName = $Arg.SubString(1)}
ElseIf ($ArgName) {$NewArgs[$ArgName] = $Arg}
}
# $NewArgs['Foo'] = 'Three test'
write-host "Running:" $Cmd $NewArgs
& $Cmd @NewArgs
Result:
PS C:\> .\wrapper.ps1 test.ps1 -foo 'one three'
Running: C:\...\test.ps1 System.Collections.DictionaryEntry
test: Args:
FOO one three
BAR
Upvotes: 1
Reputation: 7334
I actually stumbled on something that works:
& powershell.exe -file $Cmd @NewArgs
But it feels ugly to call a second powershell instance to do what I need.
Maybe there is a cleaner version.
Upvotes: 0