The Problem with PowerShell Positional Parameters
Summary: In this blog, Microsoft Scripting Guy, Ed Wilson, talks about problems that can arise when using Windows PowerShell positional parameters.
Microsoft Scripting Guy, Ed Wilson, is here. Today is the final day of judging for the 2012 Scripting Games. The judges have made a magnificent effort these last few days to get the final scripts graded. The leaderboard has changed on a minute-by-minute basis for the last several days, and I know from twitter that many of you have been following the leaderboard more closely than University of Kentucky basketball fans followed the NCAA Final Four Tournament games.
In yesterday’s blog, When You Should Use PowerShell Aliases, I discussed the problems that indiscriminate alias usage creates.
One of the main problems with using Windows PowerShell aliases, even at the Windows PowerShell console, is that the code is much less readable. The same problem exists with positional parameters, parameter aliases, and partial parameters.
Understanding positional parameters
Positional parameters make Windows PowerShell commands shorter because you do not have to do as much typing, and some of the parameter names are rather long. But the problem is that unless you really know what a cmdlet does, and how it does it, you might not know what is actually going on. An example is the following command.
S C:> copy c:1 c:3
So what does this command do? Well, it might copy the contents of the c:3 folder to the c:1 folder. Or it might copy the contents of the c:1 folder to the c:3 folder. For that matter, what is the copy command? The copy command is an alias for the Copy-Item cmdlet. Now, what is the first parameter? Is the parameter name source original, path, target, destination, or what? As a matter of fact, the first parameter is path. The second parameter is destination. To find this sort of information you need to use the Get-Help cmdlet with the Full switch. Each parameter entry includes information that states if the parameter is required, positional, or whatever. The following output displays the Path and the Destination parameter information.
Specifies the path to the items to copy.
Accept pipeline input? true (ByValue, ByPropertyName)
Accept wildcard characters? False
Specifies the path to the location where the items are to be copied.
Accept pipeline input? true (ByPropertyName)
Accept wildcard characters? false
Understanding partial parameters
Another way of speeding up working with parameters is to use partial parameter names. For example, many cmdlets have a parameter named ComputerName. Now, this is a lot of typing. It is especially unfortunate because the Get-WmiObject cmdlet does not accept ComputerName positionally. Therefore, the following command fails.
PS C:> gwmi win32_bios localhost
Get-WmiObject : Invalid query
At line:1 char:5
+ gwmi <<<< win32_bios localhost
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], ManagementExce
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Comman
This does not mean that you are limited to typing the entire ComputerName parameter name each time you want to connect to a remote computer. You only need to type enough of the parameter so that it will distinguish it from other parameters that also start with the letter C. One easy way to see what will work is to simply run the command and look at the error message. In the following output, you can see that there are three parameters on the Get-WmiObject cmdlet that begin with the letter C. The first is Class, the second one is Credential, and the last parameter is ComputerName.
PS C:> gwmi win32_bios -c localhost
Get-WmiObject : Parameter cannot be processed because the parameter name ‘c’ is ambi
guous. Possible matches include: -Class -Credential -ComputerName.
At line:1 char:5
+ gwmi <<<< win32_bios -c localhost
+ CategoryInfo : InvalidArgument: (:) [Get-WmiObject], ParameterBindin
+ FullyQualifiedErrorId : AmbiguousParameter,Microsoft.PowerShell.Commands.GetW
Based on the error message, you can safely use co as the partial parameter for ComputerName. This technique is shown here.
PS C:> gwmi win32_bios -co localhost
SMBIOSBIOSVersion : 8BET47WW (1.27 )
Manufacturer : LENOVO
Name : Default System BIOS
SerialNumber : R9FPY3P
Version : LENOVO – 1270
Consider the following issues if you use partial parameter completion:
- Partial parameters are hard to read and to understand. For example, the co that was discovered to work with the Get-WmiObject cmdlet does not look like ComputerName at all.
- The same partial parameter does not always work in all cmdlets that may use that parameter. For example, there could be partial parameter conflicts in other cmdlets that would necessitate using Com or even Comp. Of course Comp begins to look a little more like ComputerName, and therefore, it begins to be more readable.
- Partial parameters are not guaranteed to work the same way—even for the same cmdlets between versions. Because new parameters get added to existing cmdlets between versions, it can destroy previous working partial parameter names. This is not a compatibility issue because we do not guarantee that partial parameter names will ever work.
Because of the potential problems involved in working with partial parameters, it is general a good thing to avoid using them—even from the Windows PowerShell console line. Besides, when you have typed enough to make the partial parameter unique enough to use, a simple press of the Tab key completes the parameter anyway.
Understanding parameter aliases
Now we are back to the problem of aliases. However, there are a few caveats. The first is that in Windows PowerShell 2.0 parameter aliases are not documented. The way most people learn about parameter aliases is by looking at the code from someone who has learned about the parameter alias from someone else. In my Hey, Scripting Guy! Blog, Discovering PowerShell Cmdlet Parameter Aliases, I wrote some code that discovers parameter aliases and then writes the resulting aliases to a text file. The script takes a long time to run because there are many cmdlets, and many of these cmdlets have as many as 20 parameter aliases documented on them. Tab expansion does not help here, and Get-Help does not help either. The only way to find all of the parameter aliases is to read my blog—so I guess that means they are documented now.
The good thing about parameter aliases is that they are consistent among all cmdlets that use a particular parameter. Therefore, there is a parameter alias for ComputerName that is defined as cn. Anywhere you have ComputerName in a cmdlet, you can use cn instead. The advantage is that it is just as short as the co that we discovered would work for ComputerName, but with the advantage that cn actually makes sense.
Best practices around parameters
In a script, you should always use complete parameter names because with full parameter names, you actually know what the code is doing and it is more readable. You should avoid partial parameter names, and positional parameters for the same reason. Using the cn parameter alias is one that can safely be utilized because it makes sense, and it will be everywhere that the parameter itself resides. Because it cannot be deleted or overwritten (like cmdlet aliases can), the question of reliability does not come up.
When you are working interactively at the Windows PowerShell console, it is generally best to avoid positional parameters because they are very confusing, and it is easy to get them wrong. Is the target or the destination the first parameter? For simple cmdlets, such as Get-Process or Get-Service where the default parameter is Name, the positional use is fine, makes sense, and has little chance of error. The big problem with positional parameters comes into play where a cmdlet positionally accepts multiple parameters.
With partial parameters, the big problems are readability and the fact that they may not be consistent from version to version or even from cmdlet to cmdlet. Coupled with the fact that when you have a unique parameter, you can simply press the Tab key to avoid the problem altogether, partial parameters make little sense. If shortness of command is what you seek, you should find the parameter aliases and use them.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at firstname.lastname@example.org, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy