none
PowerShell Bug in Test for True? RRS feed

  • Question

  • Good Afternoon,

    I tried to put this through Connect, but it seemed unhappy about my attempts to get to the PowerShell listing. So I thought I might post this here. In a script I am testing for a returned WMI value that may or may not be $True. I test for the value of $True and then against "Other Value That Doesn't Matter". As I was running some tests, I noticed that if the returned value from WMI that I was testing was $True, the test against "Other Value that Doesn't Matter" returned a test response of $True.

    So I ran a few more tests with explicit $True value in the test to confirm it wasn't something in my WMI return causing the oddity:

    PS C:\Users\THISISME> if($True -eq "Blah"){write-host "This should not be"}
    This should not be
    PS C:\Users\THISISME> if($True -eq $False){write-host "This should not be"}
    PS C:\Users\THISISME> if($True -eq 42){write-host "This should not be"}
    This should not be
    PS C:\Users\THISISME> if($True -eq $True){write-host "This should be"}
    This should be
    PS C:\Users\THISISME> if($False -eq "Blah"){write-host "This should not be"}
    PS C:\Users\THISISME> if($False -eq $True){write-host "This should not be"}
    PS C:\Users\THISISME> if($False -eq 42){write-host "This should not be"}
    PS C:\Users\THISISME> if($False -eq $False){write-host "This should be"}
    This should be

    As you can see, if you test the value $True against anything EXCEPT $False, it returns that yes, they are indeed equal and returns $True. This is not the same if you test $False against other values. I also tested the same values in reverse, and got the results one would expect: 1/$True/True -eq $True but other values do not.

    I have included my PS Version information below (via get-host) for all version of PowerShell that I have tested on: 5.1.14393.576 and 4.0

    I tend to think that this isn't by design, but I've been wrong before so I thought I'd ask.

    Thanks!

    JT Howard

    Senior Consultant | Sogeti USA


    • Edited by JT Howard Monday, January 9, 2017 11:17 PM
    Monday, January 9, 2017 11:03 PM

Answers

  • Consider the following:

    D:\scripts> [int]''
    0
    D:\scripts> [bool]''
    False
    D:\scripts> [bool]'z'
    True
    D:\scripts> [bool]$null
    False
    D:\scripts> [bool]@()
    False
    D:\scripts> [bool]@{}
    True
    D:\scripts> [bool](New-Object PsObject)
    True
    D:\scripts>


    \_(ツ)_/

    • Marked as answer by JT Howard Wednesday, January 11, 2017 2:53 PM
    Monday, January 9, 2017 11:38 PM
    Moderator

All replies

  • Yes - in PowerShell  any value that is set is true.

    We do it this way:

    if($true){'Is true'}
    if('string'){'Is true'}
    if(0){'Is true'}

    if($false){}else{'Is False'}
    if(''){}else{'Is False'}
    if(0){}else{'Is False'}

    This is by design.  If you read any of the better books on PowerShell this will be noted very early - usually in the first two chapters.

    This is also the case in C languages and man other languages and has been part of language design since the development of C in the early 1970s.  Anyone who has a degree in Computer Science or Software Engineering can verify this for you.


    \_(ツ)_/


    Monday, January 9, 2017 11:20 PM
    Moderator
  • Consider the following:

    D:\scripts> [int]''
    0
    D:\scripts> [bool]''
    False
    D:\scripts> [bool]'z'
    True
    D:\scripts> [bool]$null
    False
    D:\scripts> [bool]@()
    False
    D:\scripts> [bool]@{}
    True
    D:\scripts> [bool](New-Object PsObject)
    True
    D:\scripts>


    \_(ツ)_/

    • Marked as answer by JT Howard Wednesday, January 11, 2017 2:53 PM
    Monday, January 9, 2017 11:38 PM
    Moderator
  • In many languages, 0 is False, and anything else (not missing or Null) is True.

    Richard Mueller - MVP Enterprise Mobility (Identity and Access)

    Monday, January 9, 2017 11:49 PM
  • Some have stated it as - "Any ValueType is always $false if it is empty."

    PS >$i = New-Object System.Int64
    PS >[bool]$i
    False

    Any ObjectType always evaluates to $true.

    $Null or any variable that is null always evaluates to $false.  This eliminets things like "if($x is not null)".  We just do "if($x)" to test it for null. This is powerfull with objects:

    if($o = Get-Process myproc -EA 0){
         $o.Name
    }else{
         'process not found'
    }


    \_(ツ)_/

    • Proposed as answer by myMDSNdn Wednesday, November 28, 2018 10:33 PM
    Monday, January 9, 2017 11:58 PM
    Moderator
  • Interesting, I hadn't run across that in the past, but I guess this is the first time I've had a query value return a Boolean True from WMI. Usually I'm testing for non-Boolean values. Very good demonstration, thank you!

    I am just curious as to why a value simply being set means that it equates Boolean True. It's not really a factual statement as this more or less states that 1=42. If it were an Ifexist test, that result makes perfect sense to me, but it's not.

    So at this point I'm showing that my background is not development, but administration. I apologize for what jrv noted is a somewhat remedial question in the world of development. Do you have any insight into the reason that the logic is set this way? I'm sure there's a reason, but it evades me.

    The best I have found is "PowerShell has a rich notion of TRUE/FALSE because it dramatically reduces the overhead and junk that you need to deal with when doing your work." in the article at

    blogs.msdn.microsoft.com/powershell/2006/12/24/boolean-values-and-operators  (can't list real links because forums are still processing my validation response)

    This explanation leaves a bit to be desired, and frankly I don't see how it reduces overhead as -ne 0/$false seems like pretty much the same effort if that were your desired test. I'm sure I'm missing something that would have been pointed out in a 101 class if I'd taken programming classes, but I try to learn something new every day. Any insight you could provide would be welcome.


    • Edited by JT Howard Wednesday, January 11, 2017 3:04 PM
    Wednesday, January 11, 2017 3:03 PM
  • I am just curious as to why a value simply being set means that it equates Boolean True. It's not really a factual statement as this more or less states that 1=42. If it were an Ifexist test, that result makes perfect sense to me, but it's not. So at this point I'm showing that my background is not development, but administration. I apologize for what jrv noted is a somewhat remedial question to anyone with a development background. Do you have any insight into the reason that the logic is set this way? I'm sure there's a reason, but it evades me.

    It is that way because it is the logical way of handling a test.  In a computer we only have two possible states.  ! or 0.  It is highly efficient to use this pattern of testing.  Set is true and not set is false everywhere in programming.


    \_(ツ)_/

    Wednesday, January 11, 2017 3:06 PM
    Moderator
  • The best I have found is "PowerShell has a rich notion of TRUE/FALSE because it dramatically reduces the overhead and junk that you need to deal with when doing your work." in the article at

    No - it is this way in most languages. PowerShell just makes it easier to use. Most new programmers fail to leverage this capability. It simplifies code and reduces the need for temporary variables. The script system or compiler is free to implement in the most efficient way. It also makes the code more understandable and readable.


    \_(ツ)_/

    Wednesday, January 11, 2017 3:10 PM
    Moderator

  • This explanation leaves a bit to be desired, and frankly I don't see how it reduces overhead as -ne 0/$false seems like pretty much the same effort if that were your desired test. I'm sure I'm missing something that would have been pointed out in a 101 class if I'd taken programming classes, but I try to learn something new every day. Any insight you could provide would be welcome.

    No - this causes the system to allocate storage and to be less understandable. That construct ccan also lead to errors. A boolean is designed to be tested directly. Why waste the extra step of a comparison.

    You are thinking about writing a program so that aa non-programmer can understand it.  This is a bad thing to do as it leads to some pretty weird logic errors that can be hard to detect.

    Programs only need to be written for trained and disciplined programmers.  They are not for public consumption.


    \_(ツ)_/


    • Edited by jrvModerator Wednesday, January 11, 2017 3:14 PM
    Wednesday, January 11, 2017 3:12 PM
    Moderator
  • Hi Howard,

    one more thing about this that you may find confusing about boolean at first:

    # This will be true
    $true -eq 42
    
    # This will be false
    42 -eq $true

    When you compare two objects of different types, PowerShell tries to convert the second object into the type of the first object.

    • In the first example, 42 will be converted into bool (which jrv already explained and showed will be $true. And $true obviously equals $true.
    • In the second example, $true will be converted to 1, and 42 is not equal 1

    Experience says it is more easily readable - and less to write by being able to write code like this:

    if ($Variable)
    {
        # ...
    }

    Without having to explicitly worry about the specific data-type contained within $variable. However you need to keep in mind just what is evaluated as $true and what as $false. For example, if the number "0" is a legitimate value that should be considered $true, you need to catch that.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Wednesday, January 11, 2017 3:33 PM
  • Yes - and assuming wee don't know what it is we get this:

    PS >$x = 42
    PS >if($x -eq $true){'true'}else{'false'}
    false
    PS >if($true -eq $x){'true'}else{'false'}
    true
    PS >if($x){'true'}else{'false'}
    true

    We shold not test against $true and $false to avoid subtle errors.  LOgicallly the only reason for testing a varaibale is to comapre it with a known or otherr quantity.  If we want to be explicit about the zero case then:

    if($x -ne 0){...}

    if($x){...}

    does this too.

    Where it really shines is here:

    if($result = GetAdUser -Filter 'samaccountname="userid"'){
         'user found'
    }else{
          'user not found'

    }

    PS >if($x=1){'true'}else{'false'}
    true
    PS >if($x=0){'true'}else{'false'}
    false
    PS >

    The evaluated result of an assignment is either null or the value of the assignment and it will trigger true/false allowing us to quickly and easily do both an assignment and a test in one statement.


    \_(ツ)_/



    • Edited by jrvModerator Wednesday, January 11, 2017 3:43 PM
    Wednesday, January 11, 2017 3:41 PM
    Moderator