Powershell filter ignored in pipeline

Viewed 76

I am writing a Chef library to make writing a custom resource for managing Microsoft MSMQ resources on Windows Server easier. Chef interfaces with Windows using Powershell 5.1.

I want to raise an error if my call to Get-MsmqQueue fails and returns $Null. To do so, I have created a filter to raise an error if the value is invalid. This seems to work if I pipeline a $Null value, but if the value is returned from Get-MsmqQueue and is $Null, it does not work.

Does anybody have an idea why line #5 does not raise an error, even if the value is equal to $Null?

#1 filter Test-Null{ if ($Null -ne $_) { $_ } else { Write-Error "object does not exist" }}

#2 $a = $Null
#3 $a | Test-Null | ConvertTo-Json     # this raises an error

#4 $a = Get-MsmqQueue -Name DoesNotExist
#5 $a | Test-Null | ConvertTo-Json     # this does NOT raise an error
#6 $Null -eq $a                        # this evaluates to $True
2 Answers

A cmdlet that produces no output doesn't actually emit $null - it (implicitly) emits the [System.Management.Automation.Internal.AutomationNull]::Value singleton that in expressions acts like $null, but in enumeration contexts such as the pipeline enumerates nothing and therefore sends nothing through the pipeline - unlike an actual $null value.

# True $null *is* sent through the pipeline.
PS> $var = $null; $var | ForEach-Object { 'here' }
here 

# [System.Management.Automation.Internal.AutomationNull]::Value is *not*.
# `& {}` is a simple way to create this value.
PS> $var = & {}; $var | ForEach-Object { 'here' }
# !! No output

As of PowerShell 7.0, [System.Management.Automation.Internal.AutomationNull]::Value can only be discovered indirectly, using obscure techniques such as the following:

# Only returns $true if $var contains
# [System.Management.Automation.Internal.AutomationNull]::Value
$null -eq $var -and @($var).Count -eq 0

This lack of discoverability is problematic, and improving the situation by enabling the following is the subject of this GitHub proposal.

$var -is [AutomationNull] # WISHFUL THINKING as of PowerShell 7.0

A different way to test it. This tests the standard output being non-null, not the exit code. I'm not testing the equality. The assignment is a side effect.

if (not ($a = Get-MsmqQueue -Name DoesNotExist)) { 
  Write-Error "object does not exist" }
Related