假设您有一个巨大的对象-它可能有也可能没有嵌套的数组/对象,
# Assuming 'user1' exists in the current domain
$obj = Get-ADUser 'user1' -Properties *
我要在该对象中搜索字符串SMTP
,不区分大小写...
我尝试了什么
$obj | Select-String "SMTP"
但它不起作用,因为匹配位于嵌套的集合内...简而言之,它位于属性$obj.proxyAddresses
中。
如果我运行$obj.proxyAddress.GetType()
,它将返回:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False ADPropertyValueCollection System.Collections.CollectionBase
做这件事最好的办法是什么?我知道您可以遍历属性并使用通配符匹配或.Contains()
手动查找它,但我更喜欢内置的解决方案。
因此,它将是对象的grep
,而不仅仅是字符串。
2条答案
按热度按时间esbemjvw1#
这里有一个解决方案。它可能非常慢,这取决于您搜索的深度;但深度1或2很适合您的场景:
示例结果:
说明
Find-ValueMatchingCondition
是一个函数,它获取给定的对象(InputObject
),并根据给定的条件递归地测试其每个属性。该功能分为两个部分。第一部分是针对条件对输入对象本身进行测试:
这就是说,如果
$InputObject
的值与给定的$Condition
匹配,则返回一个具有两个属性的新定制对象;Name
和Value
。Name
是输入对象的名称(通过函数的Name
参数传递),而Value
如您所料,是对象的值。如果$InputObject
是一个数组,则数组中的每个值都是单独评估的。传入的根对象的名称默认为"InputObject"
;但您可以在调用函数时将此值覆盖为您喜欢的任何值。函数的第二部分是我们处理递归的地方:
If
语句检查我们已经深入到原始对象的深度(即,由于每个对象的属性可能都有它们自己的属性,达到了潜在的无限级别(因为属性可能指向父对象),所以最好限制我们可以深入到的深度。这基本上与ConvertTo-Json
的Depth
参数的用途相同。If
语句还检查对象的类型。也就是说,对于大多数原始类型,该类型持有值,我们对它们的属性/方法不感兴趣(原始类型没有任何属性,但有各种方法,这些方法可能会根据$PropertyTypeToSearch
进行扫描)。同样,如果我们要查找-Condition {$_ -eq 6}
,我们不会想要所有长度为6的字符串;因此,我们不想深入到字符串的属性。此筛选器可能会进一步改进以帮助忽略其他类型/我们可以更改该函数以提供另一个可选的脚本块参数(例如$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(即,因为我们已经下降了1级&我们在0级停止),并将该成员的名称传递给函数的Name
参数。最后,对于任何返回值(即由函数的第1部分创建的定制对象,如上所述),我们将当前InputObject的
$Name
作为返回值的名称,然后返回这个修改后的对象。这确保返回的每个对象都有一个名称,表示从根InputObject到匹配条件的成员的完整路径,并给出匹配的值。jgwigjjp2#
注:此答案包含背景信息,并提供了不需要自定义功能的快速下线方法**。
更多更彻底、更系统的方法基于自定义函数的反射,参见JohnLBevan's helpful answer。
Select-String
操作字符串,当它将不同类型的输入对象强制为字符串时,它实质上是对其调用.ToString()
,这通常会产生泛型表示,如仅类型名称和通常的不属性的枚举。请注意,对象的
.ToString()
表示与PowerShell到控制台的默认输出不同,后者要丰富得多。如果您要查找的只是在对象的for-Display*字符串表示中找到一个子字符串**,则可以先通过管道连接到
Out-String -Stream
,然后再通过管道连接到Select-String
:Out-String
创建的字符串表示与默认情况下呈现给控制台的内容相同(它使用PowerShell的输出格式化系统);添加-Stream
会逐行发出该表示,而默认情况下会发出一个多行字符串。注:最新版本的PowerShell附带便利函数
oss
,它 Package 了*Out-String -Stream
:当然,此方法仅在显示模式实际显示感兴趣的数据时才起作用-请参阅下面的注意事项。
也就是说,**在显示模式中搜索可以说是
Select-String
默认应该做的事情--参见GitHub问题#10726注意事项:
Select-String
的-Context
参数来包括包围匹配的行,例如-Context 0,1
,以便也输出匹配之后的行。$FormatEnumerationLimit = -1
强制列出所有个元素**(默认情况下只显示前4个元素)。$FormatEnumerationLimit
仅在设置在全局范围内时才有效-请参阅this GitHub issue。$FormatEnumerationLimit
,就可以考虑基于John's answer中的自定义函数的更全面的解决方案了。Out-String
假定线宽是固定的;您可以使用-Width
来更改这一点,但要小心处理大数字,因为表格表示会对每一个输出行使用全宽。