Forum Discussion
Powershell: how to use an if condition with true false
- Aug 13, 2022
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:
- Swap the Boolean to be on the right side of the operator, with the object you're comparing to the left;
- 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
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:
- Swap the Boolean to be on the right side of the operator, with the object you're comparing to the left;
- 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
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)
- LainRobertsonAug 13, 2022Silver Contributor
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
- LainRobertsonAug 14, 2022Silver Contributor
Getting away from the topic of why you get the opposite result to what you may have expected and answering the question you raised of:
"What is the right way to use if condition that can receive $True or 'True or $False or 'False'"
There's a number of ways, but I would recommend keeping a string constant to the left, with the variable being checked to the right of the operand, like this:
if ("true" -eq $MyVariable)
This forces the right-hand side to be implicitly converted to the data type of the left hand side, which if you recall is possible because the Boolean data type contains a .ToString() method (but you cannot convert in the opposite direction.)
As you can see from the example below, this produces reliable outcomes.
If you are quite fussy about your data type standards and prefer to use the Boolean data for the actual comparison, and you want to cope gracefully with receiving a string variable, then I'd recommend the Boolean.TryParse() approach.
Cheers,
Lain
- pooja520Aug 20, 2022MicrosoftThis thread is an eye opener. Brilliant teaching in there. Learnt important internal and now lesser anxious with 'what the heck is going on'. Thanks for being such knowledgeable, humble and an amazing teacher all at the same time.
However, the reasons also makes me think whether to stick to powershell or go for more clearer langs.
Eg. I have one more weird behavior related to function returning, that would never happen in an programming lang. Will create a separate thread on it with the mini snippet.
Thanks a ton for your time and efforts explaining current one.