PowerShell Where对象与Where方法

时间:2018-06-20 20:56:57

标签: powershell

我注意到编写PowerShell类行有趣而奇怪的事情:

class A {

    [object] WhereObject(){
        return @(1,2) | Where-Object {$_ -gt 2}
    }

    [object] Where(){
        return @(1,2).Where( {$_ -gt 2})
    }
}

$a = new-object A
$a.WhereObject() # Throw exception Index was out of range. Must be non-negative and less than the size of the collection.

$a.Where() # Works well

看起来是设计使然。为什么这样工作?

解决方法

将“空”值显式转换为$ null的函数:

function Get-NullIfEmpty {
   param(
       [Parameter(ValueFromPipeline=$true)][array] $CollectionOrEmtpy
   )

   begin { $output = $null }

   process
   {
      if($output -eq $null -and $CollectionOrEmtpy -ne $null){
          $output = @()
      }
      foreach ($element in $CollectionOrEmtpy)
      {
          $output += $element
      }
   }

   end { return $output }
}

在这种情况下,该方法将如下所示:

[object] WhereObject() {
   return @(1,2) | Where-Object {$_ -gt 2} | Get-NullIfEmpty
}

我试图从类方法中返回一个空数组,但这也很棘手,因为对于常规函数来说,空数组也意味着“什么也没有”。如果您有像method1-> function-> method2-method1这样的调用链,则抛出相同的异常。因为该函数会将空数组转换为空。

因此,在我的情况下,转换为$ null最佳:)

2 个答案:

答案 0 :(得分:4)

.Where()运算符始终返回一个Collection<PSObject>。但是,管道情况不返回任何内容。这是一个问题,因为调用脚本块的代码期望结果Listresult.Count == 1中存在一个对象。流水线情况下没有对象,因此会出现索引超出范围错误。所以这是一个错误。我们仍然应该产生一个错误,但是应该是“非void方法必须返回一个值”或类似的错误。顺便说一句-code in question is here.

答案 1 :(得分:4)

  • (PowerShell v4 +) .Where() 方法 ,它在表达式模式 < em>始终返回[System.Collections.ObjectModel.Collection[psobject]] 的实例:

    • 如果没有输入对象匹配,则该实例就是 empty (它没有元素,并且其.Count属性返回0)。
  • 相反, Where-Object cmdlet 使用管道语义,这意味着以下输出行为:

    • 如果没有输出 (如果没有任何内容与过滤器脚本块匹配),则返回值为“空集合”,从技术上讲是[System.Management.Automation.Internal.AutomationNull]::Value单例。

    • p>
    • 如果单个 项匹配,则该项将按原样输出。

    • 如果多个项匹配,并且它们收集在变量中/作为表达式的一部分进行评估,则它们将收集在[object[]]数组中。

      li>

至于特定症状-自Bruce Payette's answer起已确认是 bug

此错误自reported on GitHub起。

内部[List[object]]实例用于收集通过内部管道执行的方法调用的输出。如果该内部管道输出“无”-即[System.Management.Automation.Internal.AutomationNull]::Value- no 对象被添加到列表中。但是,后续代码假定列表中至少有一个对象,并且盲目访问索引0,从而导致手头的错误。

更简单地再现问题:

class A {
  # Try to return [System.Management.Automation.Internal.AutomationNull]::Value
  # (which is what `& {}` produces).
  [object] WhereObject(){ return & {} }
}

$a = new-object A

$a.WhereObject() # Throw exception Index was out of range. Must be non-negative and less than the size of the collection.

至于期望的行为

如果该方法的代码返回“空集合” using C#'s default-value feature-参见this comment,似乎该修补程序将导致$null获得输出。