【问题标题】:Why is an empty PowerShell pipeline not the same as null?为什么空的 PowerShell 管道与 null 不同?
【发布时间】:2014-04-16 02:17:42
【问题描述】:

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

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

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

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

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

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

"ay.length = " + $ay.length  ### ay.length = 0
"az.length = " + $az.length  ### az.length = 1

$az[0].GetType()  ### throws exception because $az[0] is null

所以 $az 数组的长度为 1,$az[0] 为 $null。

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

【问题讨论】:

标签: powershell null


【解决方案1】:

扩展 Frode F. 的回答,“无”是 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

【讨论】:

  • 优秀的描述。我很惊讶有不止一个空值。在进一步谷歌搜索后,我找到了第三个:System.Management.Automation.Language.NullString。由于所有这些空值都是 -eq $null,我如何才能实际测试哪个空值在变量中? (并不是说这是一个常见的要求!)
  • 特殊空值并不是什么新鲜事,例如有 System.DBNull.Value。 PowerShell 不会像 null 一样对待这些其他特殊的 null 值,例如[NullString]::Value -ne $null.
  • 感谢 Test-IsAutomationNull 的更新。我发现你需要运行一个管道,检测到它不处理任何东西,并得出结论它一定收到了一个 AutomationNull,这几乎是可笑的。我想这就是当一种语言开始使用魔法值时你会得到的。干杯。
  • 现在我明白了为什么“Test-IsAutomationNull 仅适用于管道输入”。当您将 AutomationNull 作为函数参数传递时,它似乎会自动转换为普通的 $null。对吗?
  • 您可以在不使用管道的情况下测试 AutomationNull。只需将您的“空”值包装在数组外壳中。如果该数组的计数为 0,则您有一个 AutomationNull。如果该数组的计数为 1,则您有 $null。
【解决方案2】:

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

【讨论】:

  • 感谢您对 @($null) 的洞察和澄清,但 Jason 更好地解释了这两个 null 之间的区别。干杯。
猜你喜欢
  • 2021-06-28
  • 2015-01-10
  • 2016-12-10
  • 1970-01-01
  • 1970-01-01
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多