为什么空的PowerShell管道与null不同?

时间:2014-03-12 06:00:15

标签: powershell null

我试图理解@()数组构造函数的行为,我遇到了这个非常奇怪的测试。

似乎空管道的价值是"不完全"与$ null相同,即使它是-eq $ null

$x = 1,2,3,4 | ? { $_ -ge 3 }
$y = 1,2,3,4 | ? { $_ -ge 5 }
$z = $null

"x is $x"
"y is $y"
"z is $z"

if ($x -eq $null) {'x is null'} else {'x NOT null'}
if ($y -eq $null) {'y is null'} else {'y NOT null'}
if ($z -eq $null) {'z is null'} else {'z NOT null'}

$ax = @($x)
$ay = @($y)
$az = @($z)

"ax.length = " + $ax.length
"ay.length = " + $ay.length
"az.length = " + $az.length

"az[0].GetType "
$az[0].GetType()

产生以下输出

x is 3 4
y is
z is
x NOT null
y is null
z is null
ax.length = 2
ay.length = 0
az.length = 1
az[0].GetType
You cannot call a method on a null-valued expression.
At line:18 char:5
+     $az[0].GetType()
+     ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

因此$ az数组的长度为1,$ az [0]为$ null。

但真正的问题是:$ y和$ z都可能都是-eq $ null,但是当我用@(...)构造数组时,一个数组是空的,另一个包含一个$ null元素?

2 个答案:

答案 0 :(得分:6)

扩展Frode F.的回答,"没有"在PowerShell中是一个最神奇的值 - 它被称为[System.Management.Automation.Internal.AutomationNull] :: Value。以下内容的工作方式类似:

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

PowerShell在大多数地方都将值AutomaticNull.Value视为$ null,但不是在任何地方。一个值得注意的例子是在一个管道中:

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

这只会打印:

saw $null

请注意,即使您没有管道符,表达式本身也是管道,因此以下内容大致相同:

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

理解这一点,应该很清楚,如果$ y保存值AutomationNull.Value,则不会向管道写入任何内容,因此数组为空。

有人可能会问为什么$ null被写入管道。这是一个合理的问题。在某些情况下,脚本/ cmdlet需要指示"失败"没有使用例外 - 所以"没有结果"必须是不同的,$ null是用于这种情况的明显价值。

我从来没有遇到过一个人们需要知道你是否有价值"或$ null,但如果你这样做,你可以使用这样的东西:

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

答案 1 :(得分:4)

您遇到此问题的原因是$null是一个值。这是一个“没有价值”,但它仍然是一个价值。

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        : y
Description : 
Value       : 
Visibility  : Public
Module      : 
ModuleName  : 
Options     : None
Attributes  : {}


PS P:\> $z = $null


PS P:\> Get-Variable z | fl *

#Our $null variable is saved as a variable, with a $null value.

PSPath        : Microsoft.PowerShell.Core\Variable::z
PSDrive       : Variable
PSProvider    : Microsoft.PowerShell.Core\Variable
PSIsContainer : False
Name          : z
Description   : 
Value         : 
Visibility    : Public
Module        : 
ModuleName    : 
Options       : None
Attributes    : {}

@()的工作方式是,它保证结果在包装器(数组)中被传递。这意味着只要你有一个或多个对象,它就会将它包装在一个数组中(如果它不在像多个对象那样的数组中)。

$y没什么,它是一个引用,但没有存储可变数据。所以没有什么可以创建一个数组。但是,$z是一个存储变量,没有任何值(null-object)作为值。由于此对象存在,因此数组构造函数可以使用该项创建一个数组。