【问题标题】:Why do PowerShell comparison operators not enumerate collections of size 1?为什么 PowerShell 比较运算符不枚举大小为 1 的集合?
【发布时间】:2019-04-06 01:35:45
【问题描述】:

在检查变量和变量集合是否为空时,比较运算符似乎会枚举大小为 2 或更大的集合:

> if ( @( $null, $null ) -eq $null ) { $True } else { $False }
True

但它们不适用于大小为 1 的集合:

> if ( @( $null ) -eq $null ) { $True } else { $False }
False

我知道使用左侧 ($null -eq @( $null )) 进行空比较是最佳做法,但有人可以解释这里发生了什么吗?我怀疑发生了一些更微妙的事情会影响我编写的其他代码。

为什么这两个结果不同?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    以下项目评估为$false

    @()
    0
    $null
    $false
    ''
    

    在你的第一个例子中:

    @($null, $null) -eq $null
    

    计算结果为$null, $null,它是一个非零集合,所以它是$true。您可以通过以下方式观察到这一点:

    [bool]($null, $null)
    

    在您的第二个示例中,您观察到的是像第一种情况一样过滤数组,但是 返回一个标量(而不是数组),因为只有一个数组项与过滤器匹配:

    @($null) -eq $null
    

    计算结果为 @($null),但 powershell 将其计算为布尔上下文中的标量,因此它返回 $false,观察者:

    [bool]@($null)
    

    脚注:在 powershell v2 中,$null 过滤存在一个错误,它产生了左侧的 $null 比较。这个错误导致if/else 块被完全跳过。

    【讨论】:

      【解决方案2】:

      tl;dr

      在 PowerShell 条件/隐式布尔上下文中:

      • 单元素-元素数组被视为标量:也就是说,它们的唯一元素本身被解释为布尔值。[1]

      • 2+-元素数组总是$true,与它们的内容无关。


      将数组作为 LHS-eq 等可识别数组的运算符总是输出数组

      由于您的数组元素都是 $null 并且您与 $null 进行比较,因此您的比较是有效的no-op - 例如,@( $null ) -eq $null 导致 @( $null ) - 并且您的条件相当于:

      [bool] @( $null, $null ) # -> $true - array with 2+ elements is always $True
      [bool] @( $null )        # -> $false(!) - treated like: [bool] $null
      

      也许令人惊讶的是,隐式布尔逻辑管道逻辑应用于数组

      也就是说,一个单个元素数组(在概念上)是unwrapped并且它的元素被解释为一个布尔值。

      因此,[bool] @( $null ) 被视为与 [bool] $null 相同,即 $false

      通常,@( <one-and-only-element> )(或, <one-and-only-element>)在布尔上下文中被视为与<one-and-only-element> 相同。

      相比之下,如果一个数组有2个或更多元素,它总是在布尔上下文中$true,即使所有它的元素将被单独视为$false


      测试任意数组是否为的解决方法:

      基于.Count 属性的条件:

      if ( (<array>).Count ) { $true } else { $false }
      

      您可以附加-gt 0,但这并不是绝对必要的,因为任何非零值都隐含为$true

      应用于您的示例:

      PS> if ( ( @($null) -eq $null ).Count ) { $true } else { $false }
      True
      

      测试任意值是否为(标量)$null

      if ($null -eq <value>) { $true } else { $false }
      

      注意 $null 必须用作 LHS 以防止数组过滤逻辑生效,&lt;value&gt; 应该是一个数组。

      这也是为什么带有 PowerShell 扩展的 Visual Studio Code 建议“$null 应该在比较的左侧”,如果您编写类似 $var -eq $null 的内容。


      [1] To-Boolean 转换摘要

      • 标量中:
        • 以下是隐式$false

          • ''/""(空字符串)

          • 0(任何数字类型)。

          • $null

            • 陷阱:将$null 与带有-eq 的布尔值显式进行比较总是$false,即使$null 为RHS(尽管 RHS 通常会被强制转换为 LHS 的类型):

              $false -eq $null # !! $false - unlike `$false -eq [bool] $null`
              
        • 陷阱任何非空字符串的计算结果为$true

          • 例如,[bool] 'False'$true

          • 请注意,这与显式字符串解析不同:[bool]::Parse('false') 确实返回$false(和$true 用于'true',但不识别其他内容)。

        • 任何其他(非集合)类型的实例隐含为 $true,包括 [pscustomobject][hashtable] 类型(PowerShell 将其视为单个对象,而不是条目)。

      • 集合中,例如数组(更准确地说,是实现IList 接口的类集合类型 - 请参阅the source code):
        • 空的集合始终是$false,特殊的“空集合”值表示命令没有输出,[System.Management.Automation.Internal.AutomationNull]::Value

        • 陷阱单元素集合评估为:

          • 如果唯一的元素是标量布尔值
          • 如果该元素本身是一个集合$true 如果它至少有 1 个元素(不管该元素是什么)。
        • 2+元素集合总是$true

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-27
        • 1970-01-01
        • 1970-01-01
        • 2022-06-10
        相关资源
        最近更新 更多