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

为什么空的PowerShell管道与null不同?,powershell,null,Powershell,Null,我试图理解@()数组构造函数的行为,我遇到了这个非常奇怪的测试 空管道的值似乎与$null“不完全”相同,即使它是-eq$null 每个语句的输出显示在### 因此,$az数组的长度为1,$az[0]为$null 但真正的问题是:$y和$z怎么可能都是-eq$null,而当我用@(…)构造数组时,一个数组是空的,另一个包含一个$null元素?您遇到这种行为的原因是因为$null是一个值。这是一个“无价值”,但它仍然是一个价值 PS P:\> $y = 1,2,3,4 | ? { $_ -g

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

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

每个语句的输出显示在###

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


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

您遇到这种行为的原因是因为
$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
但是,它是一个存储的变量,没有任何值(空对象)。由于此对象存在,数组构造函数可以使用该项创建一个数组。

扩展Frode F的回答,“nothing”是PowerShell中最神奇的值-它被称为[System.Management.Automation.Internal.AutomationNull]::value。以下各项也将起到类似的作用:

$y = 1,2,3,4 | ? { $_ -ge 5 }
$y = [System.Management.Automation.Internal.AutomationNull]::Value
PowerShell在大多数地方都将AutomationNull.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

感谢您对@($null)的理解和说明,但Jason更好地解释了两个null之间的区别。干杯,精彩的描述。我对不止一个空值感到震惊。在谷歌进一步搜索之后,我发现了第三个:System.Management.Automation.Language.NullString。由于所有这些空值都是-eq$null,我如何实际测试变量中的哪个空值?(这不是一个常见的要求!)特殊的空值并不是什么新鲜事,例如有System.DBNull.Value。PowerShell不会像对待null一样对待这些其他特殊的null值,例如[NullString]::Value-ne$null。感谢您使用测试IsAutomationNull进行更新。我觉得很可笑的是,你需要运行一个管道,检测它没有处理任何东西,并得出结论说它一定收到了AutomationNull。我想这就是当一种语言开始使用神奇的值时所得到的。干杯。现在我明白为什么“TestIsAutomationNull只适用于管道输入”。当您将AutomationNull作为函数参数传递时,它似乎会自动转换为普通的$null。对吗?您可以在不使用管道的情况下测试AutomationNull。只需将“null”值包装到阵列存储模块中。如果该数组的计数为0,则有一个AutomationNull。如果该数组的计数为1,则有$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