PowershellWhere-Object 似乎没有过滤

问题描述 投票:0回答:1

我正在尝试做一些有关 Azure 策略的报告。我最终将过滤日期,但无法过滤任何内容,因此提供以下示例。

PS C:\>$defstrings = az policy definition list --management-group "mgsandbox"   # returns an array of strings
PS C:\>$def = ConvertFrom-Json -InputObject ($defstrings -join "`n") -depth 99  # converts to an array of PSCustomObject
PS C:\>$def.count
2070
PS C:\>$sel = Where-Object -inputobject $def -FilterScript { $_.displayName -eq "Kubernetes cluster containers should not share host process ID or host IPC namespace" }
PS C:\>$sel.count
2070
PS C:\> $def[0].displayName -eq "Kubernetes cluster containers should not share host process ID or host IPC namespace"
False

虽然我可能会在 displayName 上发现多个命中,但显然有一组非零的 displayName 与过滤器匹配,但选择却获得了所有这些。

有什么建议我的语法有什么问题吗?看起来很简单。

powershell filter parameter-passing
1个回答
1
投票

请勿将

-InputObject
参数
Where-Object
一起使用; 相反,通过管道提供输入

# Use the pipeline to provide input, don't use -InputObject
$def | Where-Object -FilterScript { $_.displayName -eq "Kubernetes cluster containers should not share host process ID or host IPC namespace" }

大多数 cmdlet 中,

-InputObject
参数只是一个实现细节,其目的是促进管道输入,不能直接有意义地使用;请参阅此答案了解更多信息,以及GitHub问题#4242进行讨论。


至于

你尝试过的

当您使用

-InputObject

时,作为集合(可枚举)的参数将
作为一个整体传递给cmdlet,而在管道中使用相同的会导致其枚举,即集合的元素是通过了,一一

一个简化的例子:

# Sample array. $arr = 1, 2, 3 # WRONG: array is passed *as a whole* # and in this case outputs *all* its elements. # -> 1, 2, 3 Where-Object -InputObject $arr { $_ -eq 2 }
也就是说,传递给 

Where-Object

 的脚本块被执行 
once,自动 $_
 变量绑定到整个 
数组,因此上面的效果相当于:

if ($arr -eq 2) { $arr }
由于 

$arr -eq 2

 在布尔上下文(
$true
条件)中计算为 
if
,因此 
$arr
 
作为一个整体被输出(尽管在输出时它被枚举),给人的印象是没有发生过滤。

  • $arr -eq 2
     计算为 
    $true
     的原因是 
    -eq
     运算符等支持 
    arrays 作为其 LHS,在这种情况下,行为会更改为 filtering,通过返回 的 子数组匹配元素而不是布尔值,以便 1, 2, 3 -eq 2
     产生 
    @(2)
    (包含一个匹配元素 
    2
     的数组),并将 
    @(2)
     强制转换为布尔值产生 
    $true
     (
    [bool] @(2)
    )。
    [1]
相反,如果隐含条件产生

$false

(例如,
$_ -eq 5
),则根本不会产生任何输出。

相比之下,如果您使用管道,您将获得所需的行为:

# Sample array. $arr = 1, 2, 3 # OK: Array elements are enumerated, i.e. # sent *one by one* through the pipeline. # -> 2 $arr | Where-Object{ $_ -eq 2 }


或者,您可以使用内在.Where()方法

绕过管道:

注意:这需要先收集内存中的所有输入;然而,特别是当数据已经在内存中时,这种方法的性能比管道方法更好:

# OK: # -> 2 (wrapped in a collection) @(1, 2, 3).Where({ $_ -eq 2 })
注意:

.Where()

 
always 会输出类似数组的 collection,即使只有一个 single 对象与过滤器匹配。但实际上,这通常并不重要。


[1] 有关 PowerShell 的布尔值强制转换规则的摘要,请参阅此答案的底部部分。

© www.soinside.com 2019 - 2024. All rights reserved.