Forum Discussion

pooja520's avatar
pooja520
Icon for Microsoft rankMicrosoft
Aug 12, 2022

Powershell: how to use an if condition with true false

 

Highlighted , how is $ True is equal to false? 

Same way, why does typecasting 'False' to boolean is evaluating to true?

 

What is the right way to use if condition that can receive $True or 'True or $False or 'False'

 

$Stat = $True
if($Stat -eq 'fal'){Write-Host "if executed"}else{Write-Host "else block"}

if($Stat -eq $False){Write-Host "if executed"}else{Write-Host "else block"}

if($Stat -eq 'False'){Write-Host "if executed"}else{Write-Host "else block"}

$Stat = 'True'
 if($Stat -eq 'False'){Write-Host "if executed"}else{Write-Host "else block"}

 if($Stat -eq 'True'){Write-Host "if executed"}else{Write-Host "else block"}

if($Stat -eq $True){Write-Host "if executed"}else{Write-Host "else block"}

 if($Stat -eq $False){Write-Host "if executed"}else{Write-Host "else block"}

$Stat = $False
 if($Stat){Write-Host "if executed"}else{Write-Host "else block"}

$Stat = 'False'
 if($Stat){Write-Host "if executed"}else{Write-Host "else block"}

 if([bool]$Stat){Write-Host "if executed"}else{Write-Host "else block"}

 $dd = [bool]$Stat
 if($dd){Write-Host "if executed"}else{Write-Host "else block"}


  • pooja520 

     

    PowerShell is not strongly-typed language like C#, where the compiler wouldn't even let you run a test like [bool] -eq [string]. Where PowerShell does allow such operations, they come with behaviours you need to know about.

     

    What your first test ($Stat -eq 'fal') is testing is for the existence (i.e. present and not null) of the right of the operator - since the types themselves don't match - and comparing that to the value on the left. So, rather than:

     

    $Stat -eq 'fal';

     

    You're actually comparing (since 'fal' both exists and is not null):

     

    $Stat -eq $true;

     

    Hence the result is $true.

     

    Here's my own example illustrating this very point using an even more complex type on the right-hand side:

     

     

    Now, if you swap the values around, you'd expect to get the same outcome, but you don't. This is because PowerShell is now testing for equality against the complex ActiveDirectorySite class, which the Boolean isn't going to match (since the test is something called a reference equality test - but this isn't important.)

     

    So, this brings me to the crux of your issue: how can you reliably test a Boolean against another Boolean? (as distinct from your example that is a Boolean against a String.)

     

    I'd posit two basic ways though there are more:

     

    1. Swap the Boolean to be on the right side of the operator, with the object you're comparing to the left;
    2. Use the .NET .Equals() method on the Boolean object to assess the object being checked.

     

     

    PowerShell's implicit existence testing can be quite a handy feature, but in the case of working with the Boolean type also requires a bit more care - and testing - to avoid unintended outcomes.

     

    Cheers,

    Lain

  • LainRobertson's avatar
    LainRobertson
    Silver Contributor

    pooja520 

     

    PowerShell is not strongly-typed language like C#, where the compiler wouldn't even let you run a test like [bool] -eq [string]. Where PowerShell does allow such operations, they come with behaviours you need to know about.

     

    What your first test ($Stat -eq 'fal') is testing is for the existence (i.e. present and not null) of the right of the operator - since the types themselves don't match - and comparing that to the value on the left. So, rather than:

     

    $Stat -eq 'fal';

     

    You're actually comparing (since 'fal' both exists and is not null):

     

    $Stat -eq $true;

     

    Hence the result is $true.

     

    Here's my own example illustrating this very point using an even more complex type on the right-hand side:

     

     

    Now, if you swap the values around, you'd expect to get the same outcome, but you don't. This is because PowerShell is now testing for equality against the complex ActiveDirectorySite class, which the Boolean isn't going to match (since the test is something called a reference equality test - but this isn't important.)

     

    So, this brings me to the crux of your issue: how can you reliably test a Boolean against another Boolean? (as distinct from your example that is a Boolean against a String.)

     

    I'd posit two basic ways though there are more:

     

    1. Swap the Boolean to be on the right side of the operator, with the object you're comparing to the left;
    2. Use the .NET .Equals() method on the Boolean object to assess the object being checked.

     

     

    PowerShell's implicit existence testing can be quite a handy feature, but in the case of working with the Boolean type also requires a bit more care - and testing - to avoid unintended outcomes.

     

    Cheers,

    Lain

    • pooja520's avatar
      pooja520
      Icon for Microsoft rankMicrosoft
      Thanks a bunch Lain for taking time explaining this. This definitely saves some of my hairs been pulled out trying figure what's going on. Thanks a ton again for taking time helping out with understandable examples.
      Understood why below is dangerous:

      $Stat = 'False' #simulation of variable value can be of type string at times and so a direct 'if' on it yields wrong result.
      if($Stat){Write-Host "if executed"}else{Write-Host "else block"}

      A followup:
      Though the swap trick seems to work however the "equals" method is not usable for my case because, my variable at times can have string at times (it's fetched from another source and by the time it reaches my code block, it can be either string or boolean and that's where ".Equals" doesn't give the desired result.
      I understand from .NET's point of view, they both are different so it's all right. However the swap trick also yields undesired result

      $Stat = 'False'
      $True -eq $Stat
      > True #how do I get here False?

      Typecasting also doesn't help as
      [bool]'False' is also True

      Whether my variable value comes a string or boolean, I want the 'if' block to be executed only if it's really True (irrespective of the data type)
      • LainRobertson's avatar
        LainRobertson
        Silver Contributor

        pooja520 

         

        No worries at all!

         

        Taking a brief look at some of your final points:

         

        The .Equals() method

        Looking at the reference documentation (below), you can see there's two overrides for the .Equals() method: one that accepts an actual Boolean value and another that accepts any object type.

         

         

        Without going into depth here, only the .Equals() that takes the Boolean parameter is of use in the majority of cases.

         

        This is necessary to remember when looking at the example you provided - which uses mixed types, which I'll do again before bringing things together.

         

        First, here's your example again as the reference point.

         

        $Stat = 'False'
        $True -eq $Stat
        > True #how do I get here False?

         

        Here's the important takeaways in short form:

         

        • Your first statement is a String assignment, not a Boolean assignment;
        • Your second statement is comparing a Boolean type on the left to a string type on the right, which should be read as:

          $True -eq ($null -ne $Stat)

          And since $Stat is indeed not a null reference, this leads to the assessment of $True -eq $True, which of course is true, which brings you to your third point of:
        • "How can we get a false instead?"

         

        Before getting to answering that, here's another reference image.

         

         

        This picture should help illustrate the difference between what I said before as well as speak to why you get $true and not $false for your third line.

         

        The if() statement highlights what I said in my previous post which is that the value held in the string, $Stat, is not implicitly converted to its Boolean equivalent. Rather, PowerShell sees some random old string and performs an existence - or "not null" - check.

         

        So, where you are expecting this assessment:

         

        $True -eq (implied conversion of string 'false' to $false)

         

        What PowerShell is actually doing is this:

         

        $True -eq ($Stat -ne $null)

         

        Which is how you get to the assessment of $True -eq $True, which of course produces the result you see of True.

         

        So, this gets us as far as explaining the result you saw but not yet how to do what you want, which is to assess a string as if it were a Boolean.

         

        It's important to mention at this point that you cannot implicitly convert a string into a Boolean value - as you noted in your comment where you tried to typecast using the [bool] prefix. This is actually one instance where PowerShell and it's cousin, C#, are consistent, since you can't do it that way there, either.

         

        Here's a very simple, single-line function that shows you how PowerShell responds to the implied string-to-Boolean conversion scenario (i.e. it throws an exception saying it cannot):

         

         

        So, now that we've seen PowerShell's "reluctance" to perform implicit casting from a string to a Boolean, how do we move forward with a proper Boolean value-to-value comparison?

         

        We're getting closer to answering that, but we're not there yet!

         

        While we cannot implicitly convert a string to a Boolean, we can implicitly convert a Boolean to a string. This behaviour explains what you see below:

         

         

        The first line equates to:

         

        $false -eq ($null -ne [string])

         

        Which is why you get a result of false where you might have been excused for expecting a true. In this example, the left and the right sides of the operand are of Boolean type.

         

        The second line, however, involves strings on both sides of the operand. In other words, the second line equates to:

         

        "false" -eq ($false.ToString())

         

        Which of course produces the expected result of true.

         

         

        Slightly modifying our earlier single-line function to require a [string] input, we can see a degree of progress.

         

         

        But we're still not there yet since a string-to-string comparison isn't what I would call robust.

         

        Enter the .Parse() and .TryParse() methods

        The Boolean structure defines a .Parse() method - well two, actually, but there's no need for discussing both.

         

         

        The key point of difference with these two is that for Parse, you should wrap that in your own try ... catch block, where TryParse avoids the need for the try ... catch block at the expense of creating an additional clause in your "if" statement.

         

        Parse() with try ... catch

         

        TryParse()

         

         

        Summary

        That's a much deeper dive than is generally needed for most situations but the general gist is to pick a data type to compare and adjust accordingly:

         

        • Compare a string to a string by placing the static string on the left and the string or Boolean variable to the right of the operand, or;
        • Compare a Boolean to a Boolean by suitably parsing the supplied string variable into a Boolean.

         

        Either of these approaches should yield consistent results.

         

        As I mentioned before, Booleans can be deceptive in PowerShell under certain circumstances.

         

        Cheers,

        Lain

Resources