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
-
and suffixed with :
$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 with N
). 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 the ValueFromRemainingArguments
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:
# Expects -Name as a direct argument and an
# -Arguments hashtable with additional name-value pairs.
function echoName {
[CmdletBinding()]
param(
[string]$Name,
[System.Collections.IDictionary] $Arguments
)
Write-Verbose -Verbose "echoName: -Name is:"
$Name
Write-Verbose -Verbose "echoName: -Arguments is:"
$Arguments
}
# Expects just a hashtable, which it passes through to
# echoName *via a splatting*
function wrapper {
[CmdletBinding()]
param(
[System.Collections.IDictionary] $Arguments
)
Write-Verbose -Verbose "wrapper: -Arguments is: "
$Arguments
echoName @Arguments
}
# Construct an ordered hashtable (dictionary) of name-value pairs.
$d = [ordered] @{
Name = 'John'
# Nested hashtable to ultimately pass to echoName's -Arguments
Arguments = @{
Age = 25
}
}
# Pass it *as-is* to wrapper - don't use splatting here.
wrapper $d
Display output: