powershell PowerScript过滤悖论

3duebb1j  于 2023-03-02  发布在  Shell
关注(0)|答案(2)|浏览(110)

在本例中,一些记录的帐户类型为N,而其他记录的帐户类型为G。
为什么筛选器在此脚本中起作用:

Get-Content "MembersMS.txt" | ForEach-Object {Get-ADUser -Identity $_ -Properties name, uht-IdentityManagement-AccountType} | Where-Object {($_['uht-IdentityManagement-AccountType'] -ne "N")}

也就是说,你只得到G条记录,而不是这条:

Get-Content "MembersMS.txt" | ForEach-Object {Get-ADUser -Identity $_ -Properties * | select name, uht-IdentityManagement-AccountType} | Where-Object {($_['uht-IdentityManagement-AccountType'] -ne "N")}

也就是说,你得到了N和G记录。
第二个脚本中额外的“管道”允许更好的格式,因为每条记录获得的数据要少得多;你会得到列式输出,每条记录一行。
我试过了--不像“N*”那样无济于事。

bvjveswy

bvjveswy1#

问题不在于属性或过滤,而在于Select语句的位置,以及它如何更改管道中的后续语句。
第一句话:

Get-Content "MembersMS.txt" | ForEach-Object {Get-ADUser -Identity $_ -Properties name, uht-IdentityManagement-AccountType} | Where-Object {($_['uht-IdentityManagement-AccountType'] -ne "N")}

可分为2个语句:

$AllUsers = $Get-Content "MembersMS.txt" | ForEach-Object {Get-ADUser -Identity $_ -Properties name, uht-IdentityManagement-AccountType}

$AllUsers | Where-Object {($_['uht-IdentityManagement-AccountType'] -ne "N")}

同样,第二个语句:

Get-Content "MembersMS.txt" | ForEach-Object {Get-ADUser -Identity $_ -Properties * | select name, uht-IdentityManagement-AccountType} | Where-Object {($_['uht-IdentityManagement-AccountType'] -ne "N")}

可分为2个语句:

$AllUsers2 = Get-Content "MembersMS.txt" | ForEach-Object {Get-ADUser -Identity $_ -Properties * | select name, uht-IdentityManagement-AccountType} 

$AllUsers2 | Where-Object {($_['uht-IdentityManagement-AccountType'] -ne "N")}

这两个语句都生成数组:

PS C:\> $AllUsers.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

PS C:\> $AllUsers2.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

但是数组 * 内部 * 的对象是不同的。

PS C:\> $AllUsers[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    ADUser                                   Microsoft.ActiveDirectory.Management.ADAccount

PS C:\> $AllUsers2[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    ADUser                                   System.Object

第一个是ADAccount类型的数组,第二个是Sytem.Object帐户的数组。如果你迭代(和过滤)array 中的项目,这是可以的。但是当你迭代/过滤 * array * 中的属性时,这是不同的。
您使用Where-Object的方式试图调用GetEnumerator()方法,类型ADAccount通常具有该方法,但System.Object没有。

PS C:\> $AllUsers | Get-Member

   TypeName: Microsoft.ActiveDirectory.Management.ADUser

Name                   MemberType            Definition
----                   ----------            ----------
GetEnumerator          Method                System.Collections.IDictionaryEnumerator GetEnumerator()
Name                   Property              System.String Name {get;}
...

PS C:\> $AllUsers2 | Get-Member

   TypeName: Selected.Microsoft.ActiveDirectory.Management.ADUser

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Name        NoteProperty string name=HAL9256

因此,它无法枚举属性并正确执行Where-Object过滤。
更好的方法是在管道末端对Select进行“格式化”:

Get-Content "MembersMS.txt" | ForEach-Object {Get-ADUser -Identity $_ -Properties name, uht-IdentityManagement-AccountType} | Where-Object {($_['uht-IdentityManagement-AccountType'] -ne "N")} } | Select name, uht-IdentityManagement-AccountType
4szc88ey

4szc88ey2#

为了解释为什么会发生这种情况,我们需要看一下ADPropertyCollection Class,这个类类似于PropertyValueCollection类,有一个Item[String]属性,基本上允许通过索引获取属性值,即:

(Get-ADUser krbtgt)['samAccountName']

可以很好地获得krbtgtsamAccountName
然而,如果我们尝试使用Select-Object来过滤对象的一些属性,对象将不再是相同的类型(在本例中为ADUser),它将是PSObject,并且PSObject没有Item[String]参数化属性:

# no longer works
(Get-ADUser krbtgt | Select-Object samAccountName)['samAccountName']

因此理想情况下,您应该使用Select-Object作为管道的最后一条语句。请注意,Get-ADUser已经支持管道标识,因此不需要ForEach-Object

Get-Content "MembersMS.txt" | Get-ADUser -Properties uht-IdentityManagement-AccountType |
    Where-Object { $_['uht-IdentityManagement-AccountType'] -ne "N" } |
    Select-Object name, uht-IdentityManagement-AccountType

同样值得注意的是,如果你使用点符号而不是索引,两个代码片段都可以工作:

Where-Object { $_.'uht-IdentityManagement-AccountType' -ne "N" }

相关问题