Both python and powershell support a form of splatting array and named arguments as function inputs, a very useful feature.
However powershell seem to be internally inconsistent somewhat. I am trying to reproduce powershell code that behaves similarly to the following python code:
def echoName(Name, *args, **kwargs):
print("Name is:", Name)
def wrapper(*args, **kwargs):
print("args:", args)
print("kwargs:", kwargs)
echoName(*args, **kwargs)
d = {
"Name": "John",
"Age": 25
}
wrapper(**d)
# args: ()
# kwargs: {'Name': 'John', 'Age': 25}
# Name is: John
As far as I am aware ValueFromRemainingArguments
is the only way to accept left over parameters in a powershell advanced function
function echoName {
param(
[CmdletBinding()]
[string]$Name,
[parameter(Mandatory = $False, ValueFromRemainingArguments = $True)]
[Object[]] $Arguments
)
Write-Host "Name is: $Name"
}
function wrapper {
[CmdletBinding()]
param(
[parameter(Mandatory = $False, ValueFromRemainingArguments = $True)]
[Object[]] $Arguments
)
Write-Host "Arguments is: $Arguments"
echoName @Arguments
}
$d = @{
Name = 'John'
Age = 25
}
wrapper @d
# Arguments is: -Name: John -Age: 25
# Name is: -Name:
I have 3 issues with powershell's output
- Arguments is now an array
- the named arguments were prefixed with
-
and suffixed with:
- this is a weird behavior at best:
$a = @(1,2,3)
wrapper @a @d
# Arguments is: 1 2 3 -Name: John -Age: 25
# Name is: 1
How can I chain and only partially consume variables as possible in python?
What is the difference between a cmdlet and a function?
Wrapper function for cmdlet - pass remaining parameters
Is there a way to create an alias to a cmdlet in a way that it only runs if arguments are passed to the alias?
Your desire is for a function to support accepting open-ended pass-through arguments and to pass them on to a different PowerShell command as named arguments, i.e. as parameter name-value pairs.
Fundamentally, PowerShell's splatting supports passing named arguments only within the following constraints:
The caller must use a hashtable whose entries are the parameter name-value pairs.
The callee must have explicit, individual parameters whose names match the entry keys of the hashtable.
N
entry in your example would bind to parameter-Name
, as long as there are no other parameters whose name starts withN
). For long-term stability, however, this convenience is best avoided.If the input hashtable has entries that do not match parameters of the callee:
They are passed as extra, positional arguments, with each name-value pair becoming two arguments: the name itself in a way that makes it look like the parameter part of a named argument (e.g.
'-Name:'
) and the value as a separate argument (e.g.'John'
)If the callee is a simple (non-advanced) function or script, these extra positional arguments are collected in the automatic
$args
variable variable.If the callee is an advanced function or script, the only way to receive these extra positional arguments is to explicitly define a catch-all parameter - which invariably only supports positional arguments - via the
ValueFromRemainingArguments
parameter-attribute property, as shown in your question. Without that, an error would occur, because advanced functions by design prevent unrecognized arguments from getting passed.The automatic
$args
variable - despite being an array rather than a hashtable - has built-in magic that allows you to pass its positionally collected arguments on as named arguments (assuming the callee is a PowerShell command).However, no custom array supports this, so if you do need an advanced function - which makes
$args
unavailable - your only option is to reconstruct a hashtable from theValueFromRemainingArguments
array's elements and use the result for splatting; however, this is not only cumbersome, but cannot be done fully robustly - see this answer for an implementation and more information.In your specific case, I suggest doing using a mix of splatting and passing (possibly ordered) hashtables directly:
Display output: