Why is an empty PowerShell pipeline not the same as null? Why is an empty PowerShell pipeline not the same as null? powershell powershell

Why is an empty PowerShell pipeline not the same as null?


Expanding on Frode F.'s answer, "nothing" is a mostly magical value in PowerShell - it's called [System.Management.Automation.Internal.AutomationNull]::Value. The following will work similarly:

$y = 1,2,3,4 | ? { $_ -ge 5 }$y = [System.Management.Automation.Internal.AutomationNull]::Value

PowerShell treats the value AutomationNull.Value like $null in most places, but not everywhere. One notable example is in a pipeline:

$null | % { 'saw $null' }[System.Management.Automation.Internal.AutomationNull]::Value | % { 'saw AutomationNull.Value' }

This will only print:

saw $null

Note that expressions are themselves pipelines even if you don't have a pipeline character, so the following are roughly equivalent:

@($y)@($y | Write-Output)

Understanding this, it should be clear that if $y holds the value AutomationNull.Value, nothing is written to the pipeline, and hence the array is empty.

One might ask why $null is written to the pipeline. It's a reasonable question. There are some situations where scripts/cmdlets need to indicate "failed" without using exceptions - so "no result" must be different, $null is the obvious value to use for such situations.

I've never run across a scenario where one needs to know if you have "no value" or $null, but if you did, you could use something like this:

function Test-IsAutomationNull{    param(        [Parameter(ValueFromPipeline)]        $InputObject)    begin    {        if ($PSBoundParameters.ContainsKey('InputObject'))        {            throw "Test-IsAutomationNull only works with piped input"        }        $isAutomationNull = $true    }    process    {        $isAutomationNull = $false    }    end    {        return $isAutomationNull    }}dir nosuchfile* | Test-IsAutomationNull$null | Test-IsAutomationNull


The reason you're experiencing this behaviour is becuase $null is a value. It's a "nothing value", but it's still a value.

PS P:\> $y = 1,2,3,4 | ? { $_ -ge 5 }PS P:\> Get-Variable y | fl *#No value survived the where-test, so y was never saved as a variable, just as a "reference"Name        : yDescription : Value       : Visibility  : PublicModule      : ModuleName  : Options     : NoneAttributes  : {}PS P:\> $z = $nullPS P:\> Get-Variable z | fl *#Our $null variable is saved as a variable, with a $null value.PSPath        : Microsoft.PowerShell.Core\Variable::zPSDrive       : VariablePSProvider    : Microsoft.PowerShell.Core\VariablePSIsContainer : FalseName          : zDescription   : Value         : Visibility    : PublicModule        : ModuleName    : Options       : NoneAttributes    : {}

The way @() works, is that it guarantees that the result is delievered inside a wrapper(an array). This means that as long as you have one or more objects, it will wrap it inside an array(if it's not already in an array like multiple objects would be).

$y is nothing, it's a reference, but no variable data was stored. So there is nothing to create an array with. $z however, IS a stored variable, with nothing(null-object) as the value. Since this object exists, the array constructor can create an array with that one item.