Why is my boolean of value 0 returning true?

后端 未结 2 558
天涯浪人
天涯浪人 2021-01-18 09:47

I\'ve been working on some PowerShell scripts and found something a little odd. I have a script that accepts 4 mandatory parameters: two strings and two Booleans.

2条回答
  •  时光说笑
    2021-01-18 10:24

    This blog by Jeffrey Snover offers some insight on the behavior of booleans in Powershell. Below is an excerpt, where he creates a simple function "test" to return true or false depending on the input parameter:

    PS> test "0"
    TRUE
    PS> test 0
    FALSE
    PS> test 1
    TRUE
    PS> test 0.0
    FALSE
    PS> test 0x0
    FALSE
    PS> test 0mb
    FALSE
    PS> test 0kb
    FALSE
    PS> test 0D
    FALSE
    PS> test 0.00000001
    TRUE
    

    “0” is TRUE because it is a STRING and it has a length of 1. 0 is FALSE because it is a number and that number is 0. In PowerShell, any number which evaluates to 0 is FALSE and every non-zero number is TRUE. The example shows you a floating point zero, a hexadecimal zero, 0 megs, 0 kilos, 0 decimal, there are all sorts of zeros but to PowerShell, they all evaluate to FALSE.

    Without any sample code it is hard to say exactly what is happening, but what we can say is that your input isn't being identified as zero by Powershell. Perhaps it is a string? This would be true if you used Read-Host to get user input. Here's an example:

    PS C:\> $test = Read-Host "Input"
    Input: 0
    PS C:\> $test.GetType()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     String                                   System.Object
    
    PS C:\> test $test
    TRUE
    
    PS C:\> $test = [Int32]$test
    PS C:\> test $test
    FALSE
    

    You can check by using GetType() on the variable in question, and fixing it may be a simple matter of explicitly casting to the desired type.

    The more I read your question -- unless I've misunderstood it -- this seems to address your problems. Especially where you comment that you've been "passing in all sorts of different things" as any non-zero length string would evaluate to true in this context.

    PS C:\> $anotherTest = "42"
    PS C:\> test $anotherTest
    TRUE
    PS C:\> $anotherTest = [Int32]$anotherTest
    PS C:\> test $anotherTest
    TRUE
    

    Edit: Alright, I've worked a bit more on the problem now that I have some idea what your environment is. First, everything I told you above is true, so please don't disregard it. The problem you're experiencing is that the boolean type conversion is handling the powershell mandatory prompt input in a way that isn't immediately obvious.

    So some situation exists where this code snippet:

    param
    (
        [Parameter(mandatory=$true)][bool]$myBool
    )
    Write-Host $myBool
    

    Will cause the following result when you use powershell's mandatory parameter prompt instead of submitting the variable on the command line:

    PS C:\> .\script.ps1 
    cmdlet script.ps1 at command pipeline position 1
    Supply values for the following parameters: 
    myBool: 0 
    True
    

    Let me re-iterate: In Powershell, all strings that are not of a null length evaluate to true. This includes "0", and this includes string literals. But what's the problem? We've already explicitly declared our variable as a bool, so it should understand that I mean 0, right?

    Wrong. A rather unfortunate situation is created where we're expecting a bool, or at least a string, when we set our input to the prompt. We do indeed eventually get out bool, but remember what happens to non-null strings when we convert them to bools? The type conversion to bool is being applied to the literal input you set at the prompt, which is not a numeric type. Since the input is of a non-null length, the bool conversion evaluates to true. You are essentially performing this operation:

    PS C:\> [bool]$myBool = [bool]"0"
    PS C:\> $myBool
    True
    

    The big problem with this is that since we've already converted our variable to a bool, the string has been consumed and we're just left with the value 1, or True. So your "0" literally turned into 1. We can't get the 0 back anymore. What should we do? I'll list a couple of options:

    • Set your variable as an [int] type instead of a [bool]. The bool conversion consumed the "0" string and turned it into a 1, so why not use a type that won't do that? Powershell understands numerical 0s and 1s to be true and false, so you can use any numerical type.

    Example with output:

    param
    (
        [Parameter(mandatory=$true)][int]$myBool
    )
    Write-Host $myBool
    
    PS C:\> .\script1.ps1
    cmdlet script1.ps1 at command pipeline position 1
    Supply values for the following parameters:
    myBool: 0
    0
    
    • If you are using the bools as logic switches, consider the [switch] parameter type instead. Switches always evaluate to false unless you explicitly set them. You shouldn't ever expose the prompt this way, so you won't encounter this problem. More info here.

提交回复
热议问题