powershell - 如何在对象中搜索值?

标签 powershell object properties powershell-v5.1

假设您有一个巨大的对象 - 一个可能有也可能没有嵌套数组/对象的对象,

# 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-JsonDepth 参数本质上是相同的目的。

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/

相关文章:

javascript - 添加/覆盖数组中的对象

Java - 不存储值的对象数组(范围?)

html - 覆盖 CSS 移除属性

c# - 属性和方法的区别

python - PowerShell 在大型搜索/替换操作中很慢(比 Python 慢得多)?

powershell - 如何从 Azure 资源管理器 Runbook 登录?

java - 将数据对象检索到 ListView 时,Firebase 返回随机值

Java Properties.load() - 如何切换键值对?

powershell - 如何清除Powershell中的变量内容

powershell - 使用start子命令为循环改进批处理文件