代码之家  ›  专栏  ›  技术社区  ›  David Gard

变量值为$null时运行的ForEach对象循环

  •  2
  • David Gard  · 技术社区  · 7 年前

    在PowerShell(5.1.14393.1944)中,当我有一个值为 $null ForEach-Object 循环尝试在对象上迭代,导致意外结果。

    在本例中,我试图从一组数据中重新运行最大值最高的结果。但是,如果没有数据, -1 应返回。

    function Get-MaximumResult
    {
        param(
            [parameter(Mandatory,Position=0)]
            [AllowNull()]
            [object]$MetricsData
        )
        if (-not($MetricsData)) { Write-Host "Debug: Variable is null" }
        [int]$maximum = -1
        $MetricsData | ForEach-Object {
           if ([int]$_.maximum -gt $maximum) { $maximum = [int]$_.maximum }
           Write-Host "Debug: Maximum: $maximum"
        }
        return $maximum
    }
    Get-MaximumResult -MetricsData $null
    

    生成的输出如下-

    调试:变量为空
    调试:最大值:0
    0

    我希望看到的是-

    调试:变量为空
    -1

    我在几年前发现了几个类似的问题,但是答案表明这个bug在版本3中已经修复。是否有其他人遇到过此错误?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Mark Wragg    7 年前

    根据注释,出现问题的原因是您正在使用 ForEach-Object cmdlet,它通过管道处理项目。

    如果您使用 foreach 语句,则不会对 $null 有价值的收藏(显然从PS版本3开始)。您可以通过以下示例看到这种行为:

    $null | ForEach-Object { write-host 'In feo' }
    
    foreach ($thing in $null) {
        write-host 'in fe'
    }
    

    我假设发生这种情况是因为 foreach公司 语句在执行任何操作之前对其使用的内容进行预先评估,如果发现集合为空,则不执行其操作。

    ForEach对象 另一方面,只是从管道中获取输入,一次处理一个,然后将任何输出发送到管道中,而不管输入是否有值。我可以看到这样一个场景,因为您可能不一定希望管道由于 $空 价值

        2
  •  0
  •   John Fouhy    3 年前

    我发现 this answer on another question 非常有帮助。

    简而言之,PowerShell中有两个不同的null:常规null和自动null。AutomationNull的目的基本上是在没有输出时停止管道。AutomationNull在各种情况下都会透明地转换为常规null,因此很难用正常的内省函数看到它。

    这里有一个简单的例子。

    $null | ForEach-Object { 'Hello world!' } # --> output: 'Hello world!'
    
    $null | Where-Object { $_ } | ForEach-Object { 'Hello world!' } # --> no output
    

    在第二行中,对象生成AutomationNull,这会导致ForEach对象在开始之前停止迭代。

    造成这种混乱的原因是$null可能是前一个命令的有效输出,我们可能希望ForEach对象处理它并继续运行。

    玩具示例:

    @(1, 2, $null, 4, 5, 6, $null, 8) | ForEach-Object {
        if ($null -eq $_) {
            Write-Host "Got null"
        } else {
            Write-Host "Got something"
        }
    }
    

    我们不希望ForEach循环在到达第一个null时停止。

    PowerShell试图成为一种强大的OO脚本语言 这是一个方便的CLI,感觉就像是被迫进行令人讨厌的妥协。