How to test for $null array in PowerShell
PowershellNullPowershell Problem Overview
I'm using an array variable in PowerShell 2.0. If it does not have a value, it will be $null, which I can test for successfully:
PS C:\> [array]$foo = $null
PS C:\> $foo -eq $null
True
But when I give it a value, the test for $null does not return anything:
PS C:\> [array]$foo = @("bar")
PS C:\> $foo -eq $null
PS C:\>
How can "-eq $null" give no results? It's either $null or it's not.
What is the correct way to determine if an array is populated vs. $null?
Powershell Solutions
Solution 1 - Powershell
It's an array, so you're looking for Count to test for contents.
I'd recommend
$foo.count -gt 0
The "why" of this is related to how PSH handles comparison of collection objects
Solution 2 - Powershell
You can reorder the operands:
$null -eq $foo
Note that -eq
in PowerShell is not an equivalence relation.
Solution 3 - Powershell
if($foo -eq $null) { "yes" } else { "no" }
help about_comparison_operators
displays help and includes this text:
> All comparison operators except the > containment operators (-contains, > -notcontains) and type operators (-is, -isnot) return a Boolean value when the input to the operator (the value > on the left side of the operator) is a > single value (a scalar). When the > input is a collection of values, the > containment operators and the type > operators return any matching values. > If there are no matches in a > collection, these operators do not > return anything. The containment > operators and type operators always > return a Boolean value.
Solution 4 - Powershell
If your solution requires returning 0 instead of true/false, I've found this to be useful:
PS C:\> [array]$foo = $null
PS C:\> ($foo | Measure-Object).Count
0
This operation is different from the count property of the array, because Measure-Object
is counting objects. Since there are none, it will return 0.
Solution 5 - Powershell
How do you want things to behave?
If you want arrays with no elements to be treated the same as unassigned arrays, use:
[array]$foo = @() #example where we'd want TRUE to be returned
@($foo).Count -eq 0
If you want a blank array to be seen as having a value (albeit an empty one), use:
[array]$foo = @() #example where we'd want FALSE to be returned
$foo.PSObject -eq $null
If you want an array which is populated with only null values to be treated as null:
[array]$foo = $null,$null
@($foo | ?{$_.PSObject}).Count -eq 0
NB: In the above I use $_.PSObject
over $_
to avoid [bool]$false
, [int]0
, [string]''
, etc from being filtered out; since here we're focussed solely on nulls.
Solution 6 - Powershell
The other answers address the main thrust of the question, but just to comment on this part...
> PS C:> [array]$foo = @("bar") > PS C:> $foo -eq $null > PS C:> > > How can "-eq $null" give no results? It's either $null or it's not.
It's confusing at first, but that is giving you the result of $foo -eq $null
, it's just that the result has no displayable representation.
Since $foo
holds an array, $foo -eq $null
means "return an array containing the elements of $foo
that are equal to $null
". Are there any elements of $foo
that are equal to $null
? No, so $foo -eq $null
should return an empty array. That's exactly what it does, the problem is that when an empty array is displayed at the console you see...nothing...
PS> @()
PS>
The array is still there, even if you can't see its elements...
PS> @().GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS> @().Length
0
We can use similar commands to confirm that $foo -eq $null
is returning an array that we're not able to "see"...
PS> $foo -eq $null
PS> ($foo -eq $null).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS> ($foo -eq $null).Length
0
PS> ($foo -eq $null).GetValue(0)
Exception calling "GetValue" with "1" argument(s): "Index was outside the bounds of the array."
At line:1 char:1
+ ($foo -eq $null).GetValue(0)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IndexOutOfRangeException
Note that I am calling the Array.GetValue
method instead of using the indexer (i.e. ($foo -eq $null)[0]
) because the latter returns $null
for invalid indices and there's no way to distinguish them from a valid index that happens to contain $null
.
We see similar behavior if we test for $null
in/against an array that contains $null
elements...
PS> $bar = @($null)
PS> $bar -eq $null
PS> ($bar -eq $null).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS> ($bar -eq $null).Length
1
PS> ($bar -eq $null).GetValue(0)
PS> $null -eq ($bar -eq $null).GetValue(0)
True
PS> ($bar -eq $null).GetValue(0) -eq $null
True
PS> ($bar -eq $null).GetValue(1)
Exception calling "GetValue" with "1" argument(s): "Index was outside the bounds of the array."
At line:1 char:1
+ ($bar -eq $null).GetValue(1)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IndexOutOfRangeException
In this case, $bar -eq $null
returns an array containing one element, $null
, which has no visual representation at the console...
PS> @($null)
PS> @($null).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS> @($null).Length
1
Solution 7 - Powershell
Watch out for switch. It will never run with a null array, for example as the output of an empty directory.
switch ( $null ) { default { 'yes' } }
yes
switch ( @() ) { default { 'yes' } } # no output
mkdir foo
switch ( dir foo ) { default { 'yes' } } # no output