Positional Parameters
Whether you know it or not, if you’ve used PowerShell, you’ve used positional parameters. In the following command the argument (c:\temp) is passed to the -Path parameter by position.
cd c:\temp
The other option for passing a parameter would be to pass it by name like this:
cd -path c:\temp
It makes sense for some commands to allow you to pass things by position rather than by name, especially in cases where there would be little confusion if the names of the parameters are left out (as in this example).
What confuses me, however, is code that looks like this:
function Test-Position{ [CmdletBinding()] Param([parameter(Position=0)]$parm1, [parameter(Position=1)]$parm2, [parameter(Position=2)]$parm3, [parameter(Position=3)]$parm4) }
In this parameter declaration, we’ve explicitly assigned positions to the first four parameters, in order.
Why is that confusing? Well, by default, all parameters are available by position and the default order is the order the parameters are defined. So assigning the Position like this makes no difference (or sense, for that matter).
It gets worse!
Even worse than being completely unnecessary, I would argue that specifying positions like this is a bad practice.
One “best practice” in PowerShell is that you should (almost) always use named parameters. The reason is simple. It makes your intention clear. You intend to bind these arguments (values) to these specific parameters.
By specifying positions for all four parameters (or not specifying any) you’re encouraging the user of your cmdlet to write code that goes against best practice.
What should I do?
According to the help (about_Functions_CmdletBindingAttribute), you should use the PositionalBinding optional argument to the CmdletBinding() attribute, and set it to $false. That will cause all parameters to default to not be allowed by position. Then, you can specify the Position for any (hopefully only one or two) parameters you wish to be used by position.
For instance, this will only allow $parm1 to be used by position:
function Test-Position{ [CmdletBinding(PositionalBinding=$false)] Param([parameter(Position=0)]$parm1, $parm2, $parm3, $parm4) }
Looking at the help for this function we see that this is true:
Because parm1 is in brackets ([-parm1]) we know that that parameter name can be omitted. The other parameter names are not bracketed (although the entire parameters/arguments are), so they are only available by name.
But wait, it gets easier
Even though the help says that all parameters are positional by default, it turns out that using Position on one parameter means that you have to use it on any parameters you want to be accessed by position.
For instance, in this version of the function I haven’t specified PositionalBinding=$False in the CmdletBinding attribute, but only the first parameter is available by position.
function Test-Position2{ [CmdletBinding()] Param([parameter(Position=0)]$parm1, $parm2, $parm3, $parm4) }
Here’s the syntax help:
That’s interesting to me, as it seems to contradict what’s in the help. Specifically, the help says that all parameters are positional. It then says that in order to disable this default, you should use the PositionalBinding parameter. This shows that you don’t need to do that, unless you don’t want any positional parameters.
As a final example, just to make sure we understand how the Position value is used, consider the following function and syntax help:
function Test-Position3{ [CmdletBinding()] Param( $parm1, $parm2, [parameter(Position=1)]$parm3, [parameter(Position=0)]$parm4) }
By including Position on 2 of the parameters, we’ve ensured that the other two parameters are only available by name. Also, the assigned positions differ from the order that the parameters are defined in the function, and that is reflected in the syntax help.
I don’t think about parameter position a lot, but to write “professional” cmdlets, it is one of the things to consider.
–Mike
Pingback: Dew Drop - October 5, 2017 (#2575) - Morning Dew