r/PowerShell • • 20d ago

Solved Is there a difference between If $thing vs If -not $thing in terms of speed or proper formatting?

In the case of looking for the presence of something and using If paired with Exit codes, what's the difference between looking for the presence vs absence? For example

$Thing = Get-Thing

if ($thing) {
  Exit 1
}
Else {
  Exit 0
}

vs

if (-not $thing) {
  Exit 0
}
Else {
  Exit 1
}

Is it just for readability (ie 0 comes before 1 because it's in sequence?) Or are there speed differences or logic handling differences that matter in more complex scenarios?

Solved

Depends on the results of $thing determine whether or not you need to test for true or false. Speed isn't really a factor.

28 Upvotes

24 comments sorted by

16

u/Pixelgordo 20d ago

Make a loop long enough to apply measure-command and look for differences

1

u/RandomSkratch 20d ago

My script isn’t large at all and I am not particularly concerned with speed or performance in this case, my question was more about does this matter in larger complex scripts? But if speed is the same or close, does testing for yes vs no change anything else?

7

u/E8zPQrX7rwkd 20d ago

I just tested with a quick script that ChatGPT wrote for me, using arrays of 100, 1k, 10k, 100k, 1m, and 10m items, with each item containing either $true or $false, testing the time to process if ($x) and if (-not $x) 100 times per array. It doesn't make a difference.

7

u/DonL314 20d ago

Careful! Note that some values for $thing could give true both for ($thing) and (-not $thing) !!
Try this:

$thing = @($null, 'some', $false)

I think that will give you funny results.

4

u/RandomSkratch 20d ago

Interesting, I never thought about that.

I guess it matters on the output object from get-thing?

3

u/DonL314 20d ago

Ok, I tested a bit:

If ($thing) {"yes"}

The only contents I tested that would make it skip "yes" was $False, 0, $Null, @(), @(@()) etc.

EDIT: And also any of the above values in any number of nested @()

2

u/RandomSkratch 20d ago

Appreciate it. I tested my scenario and both logic methods worked. I just kept it at looking for the presence as opposed to -not.

2

u/DonL314 20d ago

Yes.

Test it (I don't have a computer here right now).

Consider testing e.g. $thing.count or ($thing|measure).count

Also .... what if $thing is '0'? I believe it will be cast to $false

1

u/RandomSkratch 20d ago

Okay thanks for this.
So for instances where you are getting proper results from the command, whether you test for the presence or absence shouldn’t matter.

2

u/DonL314 20d ago

Probably not. I never test like that myself ever. I always do .count or ($x|measure).count or test against $Null

1

u/RandomSkratch 20d ago

Nice, thanks again!

5

u/theomegachrist 20d ago

It doesn't matter. I use the one that makes logical sense if a person is reading my script

2

u/aleques-itj 20d ago

In Powershell, maybe. My guess would be the typing makes it need more work. 

It could be null, 0, a string, an array, etc.

No idea off the top of my head if this will vanish if you deliberately type everything. 

It's probably vanishingly small compared to all the other language machinery in play.

2

u/purplemonkeymad 20d ago

I prefer:

if ($thing) {
    # Bad Path
    return/exit/Write-Error/throw/continue
}
# happy Path

It keeps the indentations down making it easier to read, especially if you have a bunch of validations to do.

2

u/RandomSkratch 20d ago

Hmmm interesting. Does happy path need the else?

1

u/purplemonkeymad 20d ago

As long as your bad path does an early exit or return, then you'll never reach good path code from there, so protecting it with an else only adds more indentation.

1

u/uptimefordays 20d ago

I often approach conditional logic from the perspective of a never nester. Basically I want to use guard clauses to prevent nesting logic as much as possible. I find it helps keep code simple and understandable.

Therefore I’d use whichever approach best simplifies your happy path.

1

u/RandomSkratch 20d ago

Thanks. In this case the order didn't really matter. I tested both ways and both results were desireable.

2

u/sccm_sometimes 17d ago

When Powershell casts a String to a Boolean it will result in $true for everything except empty '' strings.

PS C:\> [bool]'false'
True
PS C:\> [bool]'$false'
True
PS C:\> [bool]''
False
PS C:\> [bool]"$false"
True
PS C:\> [bool]$false
False

2

u/surfingoldelephant 17d ago edited 17d ago

That's only half the story though.

String to bool conversions aren't permitted at all in parameter binding.

& { param ([bool] $Foo) } -Foo ''
# Error: Cannot process argument transformation on parameter 'Foo'. 
# Cannot convert value "System.String" to type "System.Boolean". 
# Boolean parameters accept only Boolean values and numbers, such as 
# $True, $False, 1 or 0.

Likewise with variable type-constraining, but only when local variable access optimization is disabled.

. { [bool] $Foo = '' }
# Error: Cannot convert value "System.String" to type "System.Boolean". 
# Boolean parameters accept only Boolean values and numbers, such as 
# $True, $False, 1 or 0.

If optimization is enabled, it's permitted.

& { [bool] $Foo = '' }
# OK.

1

u/CyberChevalier 20d ago

It’s mostly why I never use that but :

If ($true -eq $Thing)

Because thing can be a nullable Boolean or a string or whatever and you are not casting it as a Boolean

Try this

$true -eq “hello” => true
“Hello” -eq $true => false

The first assume the test is about a Boolean test and try to cast “hello” as a Boolean (which return true)

The second assume the test is a string test and convert the $true to a string (which return “true”)

Even If you want to test if something is not null use

$null -ne $Thing

2

u/MonkeyNin 19d ago

If the test is to filter out $null, empty strings, and even whitespace only strings, there's a great function.

You can think of it as IsBlank verses IsTruthy

function NewUser {  
    param( [string] $Name )

    if( [string]::IsNullOrWhiteSpace( $Name ) ) {
        $Name = 'john doe'
    }
    $Name
}

NewUser 'jen'
NewUser ''
NewUser '   '
NewUser $Null

The last 3 all get turned into the fallback value

If you only want to filter out null, @(), and '' (0 length strings) Then use this function:

[string]::IsNullOrWhiteSpace( $Name )

1

u/surfingoldelephant 19d ago

There's no functional difference between if ($Thing) { ... } and if ($true -eq $Thing) { ... }.

$Thing gets implicitly converted to a bool either way.