假设您有一个巨大的对象 - 一个可能有也可能没有嵌套数组/对象的对象,
# Assuming 'user1' exists in the current domain
$obj = Get-ADUser 'user1' -Properties *
我想在该对象中搜索不区分大小写的字符串 SMTP
...
我尝试过的
$obj | Select-String "SMTP"
但它不起作用,因为匹配在嵌套的 Collection 中...简而言之,它位于属性 $obj.proxyAddresses
中。
如果我运行 $obj.proxyAddress.GetType()
它返回:
IsPublic IsSerial Name BaseType -------- -------- ---- -------- True False ADPropertyValueCollection System.Collections.CollectionBase
解决此问题的最佳方法是什么?我知道您可以遍历属性并使用通配符匹配或 .Contains()
手动查找它,但我更喜欢内置解决方案。
因此,它将是对象的 grep
而不仅仅是字符串。
最佳答案
这是一种解决方案。根据您搜索的深度,它可能会非常慢;但是 1 或 2 的深度很适合您的场景:
function Find-ValueMatchingCondition {
Param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[PSObject]$InputObject
,
[Parameter(Mandatory = $true)]
[ScriptBlock]$Condition
,
[Parameter()]
[Int]$Depth = 10
,
[Parameter()]
[string]$Name = 'InputObject'
,
[Parameter()]
[System.Management.Automation.PSMemberTypes]$PropertyTypesToSearch = ([System.Management.Automation.PSMemberTypes]::Properties)
)
Process {
if ($InputObject -ne $null) {
if ($InputObject | Where-Object -FilterScript $Condition) {
New-Object -TypeName 'PSObject' -Property @{Name=$Name;Value=$InputObject}
}
#also test children (regardless of whether we've found a match
if (($Depth -gt 0) -and -not ($InputObject.GetType().IsPrimitive -or ($InputObject -is 'System.String'))) {
[string[]]$members = Get-Member -InputObject $InputObject -MemberType $PropertyTypesToSearch | Select-Object -ExpandProperty Name
ForEach ($member in $members) {
$InputObject."$member" | Where-Object {$_ -ne $null} | Find-ValueMatchingCondition -Condition $Condition -Depth ($Depth - 1) -Name $member | ForEach-Object {$_.Name = ('{0}.{1}' -f $Name, $_.Name);$_}
}
}
}
}
}
Get-AdUser $env:username -Properties * `
| Find-ValueMatchingCondition -Condition {$_ -like '*SMTP*'} -Depth 2
示例结果:
Value Name
----- ----
smtp:SomeOne@myCompany.com InputObject.msExchShadowProxyAddresses
SMTP:some.one@myCompany.co.uk InputObject.msExchShadowProxyAddresses
smtp:username@myCompany.com InputObject.msExchShadowProxyAddresses
smtp:some.one@myCompany.mail.onmicrosoft.com InputObject.msExchShadowProxyAddresses
smtp:SomeOne@myCompany.com InputObject.proxyAddresses
SMTP:some.one@myCompany.co.uk InputObject.proxyAddresses
smtp:username@myCompany.com InputObject.proxyAddresses
smtp:some.one@myCompany.mail.onmicrosoft.com InputObject.proxyAddresses
SMTP:some.one@myCompany.mail.onmicrosoft.com InputObject.targetAddress
说明
Find-ValueMatchingCondition
是一个函数,它采用给定对象 (InputObject
) 并根据给定条件递归地测试其每个属性。
函数分为两部分。第一部分是根据条件测试输入对象本身:
if ($InputObject | Where-Object -FilterScript $Condition) {
New-Object -TypeName 'PSObject' -Property @{Name=$Name;Value=$InputObject}
}
也就是说,如果 $InputObject
的值与给定的 $Condition
匹配,则返回一个具有两个属性的新自定义对象; 名称
和值
。 Name
是输入对象的名称(通过函数的 Name
参数传递),而 Value
正如您所期望的那样是对象的值(value)。如果 $InputObject
是一个数组,则数组中的每个值都被单独评估。传入的根对象名称默认为"InputObject"
;但您可以在调用该函数时将此值覆盖为您喜欢的任何值。
函数的第二部分是我们处理递归的地方:
if (($Depth -gt 0) -and -not ($InputObject.GetType().IsPrimitive -or ($InputObject -is 'System.String'))) {
[string[]]$members = Get-Member -InputObject $InputObject -MemberType $PropertyTypesToSearch | Select-Object -ExpandProperty Name
ForEach ($member in $members) {
$InputObject."$member" | Where-Object {$_ -ne $null} | Find-ValueMatchingCondition -Condition $Condition -Depth ($Depth - 1) -Name $member | ForEach-Object {$_.Name = ('{0}.{1}' -f $Name, $_.Name);$_}
}
}
If
语句检查我们进入原始对象的深度(即,因为每个对象属性都可能有自己的属性,可能达到无限级别(因为属性可能指向后方)到父级),最好限制我们可以走多深。这与 ConvertTo-Json
的 Depth
参数本质上是相同的目的。
If
语句还检查对象的类型。即对于大多数原始类型,该类型保存值,我们对它们的属性/方法不感兴趣(原始类型没有任何属性,但有各种方法,可以根据 $PropertyTypeToSearch 进行扫描
)。同样,如果我们正在寻找 -Condition {$_ -eq 6}
,我们不会想要所有长度为 6 的字符串;所以我们不想深入了解字符串的属性。此过滤器可能会进一步改进以帮助忽略其他类型/我们可以更改函数以提供另一个可选的脚本 block 参数(例如 $TypeCondition
)以允许调用者在运行时根据他们的需要对其进行优化。
在测试是否要深入了解该类型的成员之后,我们将获取成员列表。在这里,我们可以使用 $PropertyTypesToSearch
参数来更改我们搜索的内容。默认情况下,我们对 Property
类型的成员感兴趣;但我们可能只想扫描 NoteProperty
类型的那些;特别是在处理自定义对象时。参见 https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.psmembertypes?view=powershellsdk-1.1.0有关此提供的各种选项的更多信息。
一旦我们选择了我们希望检查的输入对象的成员/属性,我们将依次获取每个成员/属性,确保它们不为空,然后递归(即调用 Find-ValueMatchingCondition
) .在此递归中,我们将 $Depth
减一(即因为我们已经下降了 1 级并且我们在 0 级停止),并将该成员的名称传递给函数的 Name
参数。
最后,对于任何返回值(即由函数的第 1 部分创建的自定义对象,如上所述),我们将当前 InputObject 的 $Name
添加到返回值的名称之前,然后返回这个修改后的对象。这确保返回的每个对象都有一个名称,表示从根 InputObject 到符合条件的成员的完整路径,并给出匹配的值。
关于powershell - 如何在对象中搜索值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52614878/