PowerShell is a type-promiscuous language. That means that objects in expressions may be converted to other types as long as the PowerShell interpreter determines that it can convert them without any loss in precision. The PowerShell interpreter does this to give you the result that it thinks you would expect. For instance, you can perform mathematical operations mixing integers, floating point numbers and string representations of numbers and get the result as if they were all entered in numeric format on a calculator. An example of this is 14 + 41.555 + “44.444”. Running this command in PowerShell will return the floating point value of 99.999. The order of these objects in the mathematical operation isn’t important because the PowerShell interpreter will determine what conversions can be done without losing the floating point precision.
Similarly, you can compare objects of different types and the comparison will evaluate to true if those objects are logically equivalent after run through the PowerShell interpreter. Or you can perform boolean logic using objects that can be converted to boolean values by the PowerShell interpreter. Here are some comparisons that evaluate to true:
1 -eq “1”
$true -eq “$true”
$true -eq “True”
$true -and “True”
All of these comparisons also evaluate to true regardless of which object is on the left-hand side of the equals operator and which object is on the right-hand side of the equals operator. This makes working with types in PowerShell very easy because the script author doesn’t have to spend as much time converting types to get the results they are looking for. Instead they are free to concentrate on the core script functionality.
Script authors need to beware, though. There is a feature in the PowerShell interpreter in PowerShell v1.0 that can result in unexpected behaviour in your PowerShell scripts if you’re not careful.
If you convert any non-empty string to a boolean value in PowerShell, the resulting boolean value will be boolean True.
Always. For every string. This happened for every string I tried, including “0”, “1”, “$true”, “$false”, “True”, “False”, “Poshoholic” — all of these will convert to boolean true if prepended with [Boolean]. This was very unexpected to me. I would have expected errors to be raised for strings that didn’t nicely convert to a boolean value (such as “Poshoholic”) because there will be a loss in precision, and for strings that do convert to a boolean value, that they would convert into the appropriate boolean value (True for “1”, “$true” and “True”, False for “0”, “$false” and “False”). I would also have expected that I could do this and get back the same value I started with:
Unfortunately that is not the case. The conversion above will return $true.
Since this has to do with converting a string to a boolean value, it is only likely to show itself in a select few circumstances, such as:
- Using the string representation of False by itself in an expression. e.g. if (“$false”).
- Comparing a boolean value to a string representation of False. e.g. $myBooleanValue -eq “$false”
In the first example, the script author might expect that the if statement would fail but instead it succeeds. In the second example, the script author might expect that the if statement would succeed if $myBooleanValue was equal to boolean False, but it will fail. This can result in scripts running incorrectly.
Fortunately, there are a few ways to workaround this. The PowerShell interpreter converts the string into a boolean value when the boolean value is the first in the comparison. Similarly, the PowerShell interpreter will convert a boolean into a string value when the string is the first in the comparison. Since the boolean values of true and false convert correctly into “True” and “False”, respectively, a script author simply has to switch the sides in the comparison to make it work. So, for the two examples shown above, they can be corrected as follows:
- “$false” -eq $false
- “$false” -eq $myBooleanValue
Alternatively, you can just pass your string representation of a boolean value into a System.Convert static method called ToBoolean, like this:
This will give you back the boolean value of $false as you might have been expecting.
If you are authoring PowerShell scripts and working with expressions containing mixed types, I recommend testing those expressions to ensure that they are giving you the results you would expect. It’s very likely that they are, but as this article illustrates there are some cases where they might not be working as expected.
That’s it for this post. Thanks for reading!
Well, not me really. This blog. I recently purchased http://www.poshoholic.com, and I’m updating my blog service to start using the new domain name, which seems more fitting, instead of the current one. Since this is software, and since moving anything rarely goes off without a hitch, I’m skeptical to think that this will be a perfectly smooth ride. If you aren’t able to receive updates after the move for whatever reason, please let me know through comments on this blog. Alternatively, if you’ve done this yourself already with your own blog and want to give me a heads up on something I should be aware of before making the switch, you can let me know that through the comments too!
I’m going to wait until late tomorrow to commit the changes so that existing subscribers will be able to get this message using the current configuration.
Looking forward to seeing you on the other side,
Keith Hill has started an “Effective PowerShell” series on his blog that allows you to learn from his experiences working with PowerShell for the past couple of years. He has published 6 articles in this series so far, including:
- Effective PowerShell Item 1: The Four Cmdlets That are the Keys to Finding Your Way Around PowerShell
- Effective PowerShell Item 2: Use the Objects Luke. Use the Objects!
- Effective PowerShell Item 3: Know Your Output Formatters
- Effective PowerShell Item 4: Commenting Out Lines in a Script File
- Effective PowerShell Item 5: Use Set-PSDebug -Strict In Your Scripts – Religiously
- Effective PowerShell Item 6: Know What Objects Are Flowing Down the Pipe
These articles are very similar to the articles in my Essential PowerShell series. There is a lot of useful information in these articles. I encourage you to take the time and read them.
While this topic may be the subject of debate and it has certainly been discussed to some extent before, I am a firm believer that PowerShell script authors should completely avoid shorthand in their shared PowerShell scripts. Shorthand should only be used when using PowerShell interactively from the console. I consider this a best practice when working with PowerShell. There are four reasons for this that all boil down to providing a better user experience: readability, integrated help support, portability and upgradability. I’ll explain.
PowerShell scripts written without any shorthand are as close to self-documenting as you can get. If the cmdlets and parameters (and variables!) being used are intelligently named, you should be able to read a script containing cmdlets and/or parameters (and variables!) that you haven’t used before and have a general idea what the script is going to do. PowerShell’s verb-noun format for cmdlets goes a long way to facilitate that. Having readable scripts is also necessary for maintenance reasons so that scripts maintained by multiple authors are understandable to all authors.
Integrated Help Support
PowerShell is still new and will likely be new for a while yet. Many people new to PowerShell will use sample scripts to learn the language, and it will be much easier for them to learn from sample scripts if they use full cmdlet and parameter names. That way they can take full advantage of the rich help system that is integrated within PowerShell by using it to learn more about PowerShell scripts they find on the web or in products that expose integrated PowerShell scripts such as PowerGUI. To illustrate this point, let’s look at a sample script where using shorthand can harm the user experience of other PowerShell users.
Here’s a sample script using shorthand that will retrieve the expanded parameter sets for the get-command cmdlet:
gcm -name get-command -type cmdlet | select -expand parametersets
And here’s that same sample script without any shorthand:
Get-Command -name Get-Command -commandType Cmdlet | Select-Object -expandProperty ParameterSets
If someone new to PowerShell is trying to figure out what the different parts of this script do, they can use the get-help cmdlet or help alias on the parts of the script. This can be especially important if English isn’t their first language (the integrated help documentation in PowerShell has already been localized in many different languages to reduce the learning curve for people whose first language is not English). For the sample script, they might try to look up help for the gcm command, the name parameter, the type parameter, the select command, or the expand parameter. Here are the PowerShell commands to do just that:
- Get-Help gcm
- Get-Help gcm -parameter name
- Get-Help gcm -parameter type
- Get-Help select
- Get-Help select -parameter expand
Of these 5 commands, 2 will fail.
The third command fails because the Get-Command cmdlet does not have a Type parameter. Type is the alias for the CommandType parameter, and PowerShell 1.0 does not resolve alias names when passed in as the value of the Parameter parameter.
The fifth command fails because the Select-Object cmdlet does not have an expand parameter. But expand isn’t an alias for a parameter either. In PowerShell 1.0, part of the parameter name resolution logic includes support for identifying parameters by the shortest substring that uniquely identifies the parameter or an alias to the parameter when compared with a list of parameters and aliases for the cmdlet. In this case, expand is a substring of expandProperty and there are no other parameters beginning with “expand”, so PowerShell deduces that the script author is referring to expandProperty and lets the script run accordingly without warnings or errors.
Had the script been written without any shorthand, as in the second sample, then all attempts to look up the same help information would succeed. Here are the same Get-Help commands but without any shorthand:
- Get-Help Get-Command
- Get-Help Get-Command -parameter name
- Get-Help Get-Command -parameter commandType
- Get-Help Select-Object
- Get-Help Select-Object -parameter expandProperty
All 5 of these commands work as expected.
As Jeffrey Snover indicated in his blog post titled “Is it safe to use ALIASES in scripts?“, aliases are not constant and can be removed. While this would likely only happen rarely in practice, PowerShell scripts using aliases are not guaranteed to be portable to other environments and should be avoided.
As I mentioned earlier in this post, part of the parameter name resolution logic in PowerShell 1.0 includes support for identifying parameters by the shortest substring that uniquely identifies the parameter or an alias to the parameter when compared with a list of parameters and aliases for the cmdlet. This means we could have written the above sample script like this:
gcm -na get-command -ty cmdlet | select -exp parametersets
In this case, “na” is the shortest substring that uniquely identifies the name parameter, “ty” is the shortest substring that uniquely identifies the type alias for the commandType parameter, and “exp” is the shortest substring that uniquely identifies the expandProperty parameter. While this works fine just now, you cannot depend on this sample continuing to work in future releases of PowerShell. Why? Because there is no guarantee that another parameter or alias will not be added in a future release that would make one of these substrings ambiguous. In fact, you cannot depend on this sample working in the current release of PowerShell for users who have added parameter aliases that would make one of these substrings ambiguous either.
For these four reasons, PowerShell authors should be diligent about avoiding use of shorthand in shared PowerShell scripts. While using aliases for cmdlets, parameters and functions and shorthand to identify parameters is very useful when using PowerShell interactively, it can negatively impact the user experience of others when used in PowerShell scripts and therefore should be avoided (with few exceptions, if any).