Consider the following:
$psversiontable # 7.4.6
$a = $null
$null -eq $a # Returns $true
$a.GetType() # You cannot call a method on a null-valued expression.
$b = $a.PSObject.Copy() # You cannot call a method on a null-valued expression.
$c = Get-Process | Where-Object { $_.Name -eq "oiawmij3209j23oifnoeicn" } # Matches nothing, should return nothing
$null -eq $c # Returns $true
$c.GetType() # You cannot call a method on a null-valued expression.
$d = $c.PSObject.Copy() # Works
$d.GetType() # PSCustomObject
My questions:
$a
and $c
in this situation? They both apparently equate to $null
.$c.PSObject.Copy()
work when $a.PSObject.Copy()
doesn't?$c.PSObject.Copy()
work when $c.GetType()
doesn't?Where-Object
, or to do with PSObject.Copy()
, or something else?Thanks for your time.
Edit: a comment asked why I'm using the PSObject property. It's to avoid this problem:
# $hash2 is a "copy" of $hash1, but both variables actually just contain references to the same object in memory, so modifying $hash2 also modifies $hash1
$hash1 = @{ foo = "apple" }
$hash2 = $hash1
$hash2.bar = "banana"
Write-Host $hash1.bar # Returns "banana"
# $hash4 is a "real" copy of $hash3, i.e. each variable contains a reference to a different object in memory, thus modifying $hash4 does NOT also modify $hash3
$hash3 = @{ foo = "apple" }
$hash4 = $hash3.PSObject.Copy()
$hash4.bar = "banana"
Write-Host $hash3.bar # Returns nothing
In the relevant production code there may be better ways to work around this, but that's how I'm currently doing it, and what spawned the question.
This question was originally (and reasonably) closed as a duplicate of the following questions:
However @mklement0 and others provide a lot of details in comments in this question which deserve to be captured in a more visible and collected answer, though they may be repeated somewhat in mklement0's answers to the other questions. As I (OP) am the learner in this situation, feel free to edit the answer/provide corrections.
The core of this question (and the linked duplicates) revolves around the technical differences between $null
and "automation null" ([System.Management.Automation.Internal.AutomationNull]::Value
), a.k.a. "the enumerable null".
To answer the direct questions in the OP:
What is the difference between $a
and $c
in this situation?
$a
is $null
, while $c
is automation null. Automation null is returned via a pipeline that has no results, which can be achieved in different ways, as demonstrated by the different questions:
Get-Process | Where-Object { $_.Name -eq "oiawmij3209j23oifnoeicn" }
1,2,3,4 | ? { $_ -ge 5 }
& { }
Why does $c.PSObject.Copy()
work when $a.PSObject.Copy()
doesn't?
$c
, as automation null is technically a [PSObject]
with a PSObject
property, while $a
is just $null
.$c.PSObject
can be accessed is arguably a bug, see further context below.Why does $c.PSObject.Copy()
work when $c.GetType()
doesn't?
$c
.$c
is effectively being evaluated to $null
, and is thus equivalent to $null.GetType()
.What causes this? Is it something to do with Where-Object, or to do with PSObject.Copy(), or something else?
Where-Object
returns no results.How can I/should I reliably differentiate between/test for these cases?
[PSObject]
, and so detecting it, and differentiating it from $null
can be achieved with ($null -eq $value) -and ($value -is [PSObject])
.($null -eq $value) -and (@($value).length -eq 0)
, or ($null -eq $value) -and (@($value).count -eq 0)
.
0
, while that of @($null)
will be 1
(for reasons someone more experienced would have to explain).Further context:
$null
in expressions, but acts differently in enumeration contexts such as the pipeline, where it acts like an empty collection and therefore enumerates nothing.(& {}).psobject
and (& {}) -is [psobject]
- is arguably a bug; a leaky implementation of an abstraction which reveals the fact that it is technically an object (of type [psobject]
a.k.a. [System.Management.Automation.PSObject]
, namely the aforementioned [System.Management.Automation.Internal.AutomationNull]::Value
singleton).$value -is [System.Management.Automation.Null]
.