PowerShell pass all parameters received in function, and handle parameters with spaces
I am a novice with PowerShell.
In Msys2 (or Lnux), I have defined a function npp
npp ()
{
${NPP_PATH} "$@"
}
such that if I call from the command prompt npp
it launches Notepad++ (as defined in ${NPP_PATH}
).
If I call npp "mydir/stage 1/a.txt"
it opens that file for editing.
Generally speaking, it allows:
- Any number of parameters.
- Parameters containing spaces, if suitably escaped.
What would be the equivalent in PowerShell?
I guess in PS I should also go for a function to obtain a similar behavior.
So far, I could receive an undefined number of parameters, and use them in a foreach
loop, see code below.
But I could not find the equivalent of the simple "$@"
to pass all parameters as they are received.
Moreover, if I use quotes in one of the arguments, they are removed, so it will probably have problems with file paths including blanks.
function multi_params {
param(
[Parameter(
ValueFromRemainingArguments=$true
)][string[]]
$listArgs
)
$count = 0
foreach($listArg in $listArgs) {
'$listArgs[{0}]: {1}' -f $count, $listArg
$count++
}
}
Assuming that NPP_PATH
is an environment variable, the equivalent PowerShell function is:
function npp {
& $env:NPP_PATH $args
}
If NPP_PATH
is the name of a regular PowerShell variable, use & $NPP_PATH $args
.
&
is the call operator, which is needed for syntactic reasons whenever you want to invoke a command whose name/path is specified in quotes and/or via a variable.
In simple functions (as opposed to advanced functions) such as the above (use of neither [CmdletBinding()]
nor [Parameter()]
attributes), you can use the automatic $args
variable to pass any arguments through to another command.
-
If the target command is not an external program, such as here, but a PowerShell command, use the form
@args
to ensure that all arguments - including those preceded by their parameter names - are properly passed through - see about_Splatting.
Note that the form@args
works with external programs too, where it is generally equivalent to$args
(the only difference is that only@args
recognizes and removes--%
, the stop-parsing token) -
Note that passing arguments with embedded
"
chars. and empty arguments to external programs is still broken as of PowerShell v7.0 - see this answer.
Passing arguments through in simple vs. advanced functions (scripts):
-
In simple functions only,
$args
contains all arguments that did not bind to declared parameters, if any, on invocation.-
If your simple function doesn't declare any parameters, as in the example above,
$args
contains all arguments passed on invocation. -
If your simple function does declare parameters (typically via
param(...)
),$args
contains only those arguments that didn't bind to declared parameters; in short: it collects any arguments your function did not declare parameters for. -
Therefore,
$args
is a simple mechanism for collecting arguments not declared or known in advance, either to be used in the function itself - notably if declaring parameters isn't worth the effort - or to pass those arguments through to another command. -
To pass arguments that comprise named arguments (e.g.,
-Path foo
instead of justfoo
) through to another PowerShell command, splatting is needed, i.e. the form@args
.- Note that while
$args
is technically a regular PowerShell array ([object[]]
), it also has built-in magic to support passing named arguments through; a custom array can not be used for this, and the hash-table form of splatting is then required - see about_Splatting
- Note that while
-
-
In advanced functions,
$args
is not available, because advanced functions by definition only accept arguments for which parameters have been declared.-
To accept extra, positional-only arguments, you must define a catch-all
ValueFromRemainingArguments
parameter, as shown in the question, which collects such arguments in an array-like[1] data structure by default. -
To also support named pass-through arguments, you have two basic option:
-
If you know the set of potential pass-through parameters, declare them as part of your own function.
- You can then use splatting with the
$PSBoundParameters
dictionary (hash table) - see below - to pass named arguments through, possibly after removing arguments meant for your function itself from the dictionary.
- You can then use splatting with the
-
This technique is used when writing proxy (wrapper) functions for existing commands; the PowerShell SDK makes duplicating the pass-through parameters easier by allowing you to scaffold a proxy function based on an existing command - see this answer.
-
-
Otherwise, there is only a suboptimal solution where you emulate PowerShell's own parameter parsing to parse the positional arguments into parameter-name/value pairs - see this answer.
-
The automatic $PSBoundParameters
variable is a dictionary that is available in both simple and advanced functions:
-
$PSBoundParameters
applies only if your function declares parameters, and contains entries only for those among the declared parameters to which arguments were actually bound (passed) on invocation; the dictionary keys are the parameter names, albeit without the initial-
.
Note that parameters bound by a default value are not included - see this GitHub issue for a discussion.
Again, note that in advanced functions you can only pass a given argument if a parameter was declared for it, so any argument passed in a given invocation is by definition reflected in$PSBoundParameters
. -
Because it is a dictionary (hash table), it can be used with hash-table based splatting -
@PSBoundParameters
- to pass named arguments through to other PowerShell commands and, since it is mutable, you have the option of adding or removing named arguments (such as the ones intended for your function itself).
[1] That type is [System.Collections.Generic.List[object]]
; however, you can specify a collection type explicitly, such as [object[]]
to get a regular PowerShell array.