1

I was trying to understand how the constants $TRUE, $FALSE and $NULL work in Powershell, and how I should test for them or compare them with variables, respectively.

Being a Powershell newbie, I did some basic tests. While $TRUE and $FALSE behaved as expected, I was baffled by what I saw when I tried to assign another value to $NULL:

PS C:\Users\Administrator> $NULL=1

PS C:\Users\Administrator> $NULL="FOO"

PS C:\Users\Administrator> $NULL

PS C:\Users\Administrator> 

$NULL should be constant or read-only, shouldn't it? So why can I assign another value to it without Powershell throwing an exception, and why is that assignment silently ignored?

In contrast, $FALSE behaves as expected:

PS C:\Users\Administrator> $FALSE=1
Cannot overwrite variable false because it is read-only or constant.
At line:1 char:1
+ $FALSE=1
+ ~~~~~~~~
    + CategoryInfo          : WriteError: (false:String) [], SessionStateUnauthorizedAccessException
    + FullyQualifiedErrorId : VariableNotWritable
 

PS C:\Users\Administrator> 
Binarus
  • 4,005
  • 3
  • 25
  • 41
  • 4
    Because `$null` is not a constant - it's a special variable that carries multiple responsibilities - it resolves to `null`, it acts as a `[void]`-sink for assignments (like in your post) and it functions as a replacement for the NUL device in cmd: `$anyValue > $null` – Mathias R. Jessen Sep 10 '20 at 18:14
  • 3
    Run `Get-Variable False | fl` and `Get-Variable null | fl`. You will see they are not built the same. – AdminOfThings Sep 10 '20 at 18:16
  • 2
    An aside: `$null = "foo"` is equivalent to `"foo" | Out-Null` or `[Void]"Foo"`. The first pattern is allegedly the fastest of the 3. In PowerShell 7 `Out-Null` was improved and is allegedly the fastest. In my experience prior to 7, it's quite a bit faster than `Out-Null` but not discernibly faster than casting to `[Void]`. – Steven Sep 10 '20 at 18:25
  • 2
    If you're just learning about PowerShell,, watch out for how comparison to ```$null``` works as well, For example ```$null -eq @()``` returns ```$false``` but ```@() -eq $null``` returns ```$null``` as it will return any *items* that match the right-hand side if the left-hand value is enumerable - e.g. ```@("aaa", "bbb") -eq "bbb"``` returns ```"bbb"```. The rule of thumb is when comparing to ```$null``` is put ```$null``` on the left. – mclayton Sep 10 '20 at 18:36
  • 4
    @Steven, good points, but note that `Out-Null` in PS [Core] v6+ has been optimized only when you discard an _expression's_ output, which, however is an atypical use case. For the typical use case of discarding a _command's_ output it is still the slowest option - see the footnote I've added to my answer. – mklement0 Sep 10 '20 at 18:53
  • 2
    @ mklement0 That's a great point. I just tested your sample. expression took ~100ms without `Out-Null` and a diminutive 1.3 ms with it. `Write-Output` took 405 ms without and a whopping 1410 with! – Steven Sep 10 '20 at 19:04
  • Thank you very much for all the insights! Regarding the comparison with `$null`, I had already read about it; Regarding performance, this is very interesting and of practical use, but for the moment not in my case; Regarding `Get-Variable null | fl`, this was really enlightening - the fact that assignments to `$null` are silently ignored is directly in the description. If I only had known that before ... – Binarus Sep 11 '20 at 08:24
  • @mclayton Not exactly correct. Comparing arrays with scalars (as in `@() -eq $null`) produces arrays of results. An empty input array produces an empty result array, not `$null`. Also, `@("aaa", "bbb") -eq "bbb"` evaluates to `@("bbb")` not `"bbb"`. Proof: `(@() -eq $null).gettype()` produces `Object[]` while `$null.gettype()` produces an `InvokeMethodOnNull` error. Also, `(@("aaa", "bbb") -eq "bbb").length` is **1** (one item array) but `"bbb".length` is **3** (3 character string). (PSVersion 5.1.19041.2364) – Uber Kluger Apr 12 '23 at 04:28
  • Some might ask "Why check an actual array against `$null`? It should always return `$false` even if it is empty. An empty array still exists, right?" But what if it were a variable containing the results of a function or command. It **might** be `$null`, a scalar or a (possibly empty) array. See [Empty Null](https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-null#empty-null) for the distinction between `$null` and _nothing_ / _empty_. Though don't take this article verbatim. There are several over simplifications (and some actual errors). – Uber Kluger Apr 12 '23 at 05:06
  • @UberKluger - true. I guess my point was just that equality comparison with ```$null``` is sometimes non-obvious, which I proved by proceeding to get it wrong as well :-). – mclayton Apr 12 '23 at 12:10
  • @mclayton I suspect that the some of the confusion stems not from `$null` itself but from the way collections (e.g. arrays) which contain `$null` are converted to boolean (such as in an `if` statement). `[bool]@()` is `$false`, `[bool]@(oneitem)` is `[bool]oneitem` and `[bool]@(multiple, items)` is `$true`. Try evaluating `if ($array -ne $null)` for the cases a) $array is `$null`, b) $array is `@()` or contains only `$null`, c) $array has only one non-`$null` element but it might be 0 or "", d) $array has multiple non-`$null` elements. This is unlikely to be the intent of that `if` statement. – Uber Kluger Apr 13 '23 at 08:04

1 Answers1

3

The primary reason that you can assign to $null - even though as the representation of a null value it should be constant (as other PowerShell automatic variables such as $true and $false are, for instance) - is that it enables the following useful idiom for output suppression:

# Discard the success output from a command.
$null = Get-Item -ErrorAction Stop foo.txt

That is, $null can act like a regular read-write variable that you can assign to, but whatever you assign to it (a command's success output, from stream number 1 - see about_Redirection) is quietly discarded.

Effectively, $null = ... is the equivalent of >NUL (1>NUL) in cmd.exe and >/dev/null (1>/dev/null) in POSIX-compatible shells such as bash.

Note that in PowerShell you could alternatively use ... | Out-Null or > $null, though the $null = ... idiom is faster than Out-Null[1] and also signals the intent to discard the (success) output up front (unlike > $null). (There's also [void] (...), but it requires you to enclose the command in parentheses.) See this answer for more.

However, you do need redirection if you also want to suppress other output streams (too); e.g.,
*> $null discards the output from all streams.


As for inspecting the properties of variables, including automatic ones, use the Get-Variable cmdlet and pass it the name of the variable without the $ sigil; e.g., null to inspect $null.

PS> Get-Variable null | Format-List

Value       : 
Description : References to the null variable always return the null value. Assignments have no effect.
Options     : None
Name        : null
Visibility  : Public
Module      : 
ModuleName  : 
Attributes  : {}

Format-List * ensures that that all properties of the variable object (a System.Management.Automation.PSVariable instance or an instance of a derived class) are listed, in list form.

A constant variable such as $false would show Constant as part of the Options property value.


[1] Note: PowerShell [Core] v6+ has an optimization that makes Out-Null the fastest solution if you discard an expression's value (e.g., 1..1e6 | Out-Null vs. a command's (e.g., Write-Output (1..1e6) | Out-Null), but note that suppressing command output is the much more common use case.

mklement0
  • 382,024
  • 64
  • 607
  • 775