Essential PowerShell: Beware of promiscuous types
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!