Why does Powershell not throw an error when we do $NULL="FOO"?

340 views Asked by At

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> 
1

There are 1 answers

0
mklement0 On BEST ANSWER

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.