PowerShell

时间:2018-02-18 17:23:18

标签: powershell active-directory

以下内容对我来说似乎很奇怪:

$user1 = Get-ADUser sameuser
$user2 = Get-ADUser sameuser
$user1 -eq $user2                # -> false
# the same for groups:
$group1 = Get-ADGroup samegroup
$group2 = Get-ADGroup samegroup
$group1 -eq $group2              # -> false

实际上,Powershell用户似乎很高兴1 -eq 1是真的。也:

"1" -eq 1           # -> true
@("1") -contains 1  # -> true

可是:

$h1 = @{bla = 1}
$h2 = @{bla = 1}
$h1 -eq $h2                          # -> false
$h1.GetHashCode(), $h2.GetHashCode() # -> 60847006, 5156994
# the above return values of course vary
$a1 = @(1;2;3)
$a2 = @(1;2;3)
$a1.GetHashCode(), $a2.GetHashCode() # -> 52954848, 34157931
# surprise, surprise:
$a1 -eq $a2           # no return value at all? (tested with versions 4.0 and 5.1)
($a1 -eq $a2).GetType()              # or an Array?
($a1 -eq $a2).count                  # -> 0

除了这些有趣的行为之外,真正令人沮丧的是我不能简单地这样做:

$ones      = Get-ADPrincipalGroupMembership one
$seconds   = Get-ADPrincipalGroupMembership second
$excl_ones = $ones | ? { $_ -notin $seconds }

但必须做这样的事情:

$second_nms = $seconds | % name
$excl_ones = $ones | ? { $_.name -notin $second_nms }

我错过了什么吗?

1 个答案:

答案 0 :(得分:8)

要理解你看到的一些奇怪的事情,我们必须退一步考虑更大的图景,即框架PowerShell建立在.NET之上!

.NET中的对象平等

$user1 -eq $user2失败,因为$user1$user2 两个不同的对象 - 尽管他们可能代表在Active中的同一个对象目录。

当谈到.NET中的对象相等时,您需要区分值相等引用相等

值类型的两个变量,例如[int],如果它们的基础值相同,则被视为相等:

$a = 1
$b = 1
$a.Equals($b) # $true

引用类型的两个变量 - 任何不是值类型的变量 - 如果它们具有相同的标识,通常只被认为是相等的 - 也就是说,它们引用内存中的同一个对象:

$a = New-Object object
$b = New-Object object
$a.Equals($b) # $false

我们知道,$a$b完全相同,但它们指的是内存中两个不同的[object]实例。

类型定义可以覆盖GetHashCode()(用于确定对象的身份的函数)和Equals()(用于确定两个对象之间相等的函数) ),因此您可能会发现某些引用类型在比较时似乎充当了值类型 - [string]是一个主要示例:

$a = "test"
$b = "test"
$a.Equals($b)

ADEntity类(ActiveDirectory模块中所有输出对象的基本类型)不会尝试这样的事情,这就是你看到结果的原因。

PowerShell中的集合过滤

上述内容并没有解释你提出的另一个奇怪的事情,即:

$a1 = @(1;2;3)
$a2 = @(1;2;3)
$a1 -eq $a2 # NOTHING! WHAT'S GOING ON HERE?

要了解这里发生了什么,您需要在PowerShell中研究比较运算符行为!

所有比较运算符(-eq-ne-gt-like-match等)根据左侧支持两种不同的模式side argument:标量过滤

标量比较

在标量模式下,比较运算符将单个对象作为其左侧操作数,将值表达式作为其右侧操作数,并返回布尔结果:$true$false

使用比较运算符过滤

在过滤模式下,比较运算符将集合(数组或列表)作为其左侧操作数,将值表达式作为其右侧操作数(就像之前一样)并返回左手集合中满足比较的所有个人成员

要查看此操作,请尝试以下操作:

$names  = "James","Jane","John"
$prefix = "Ja"

$names -like "$prefix*"

您会看到-like操作返回两个字符串 - JamesJane

如果我们将这些新发现的知识应用于您的示例

@(1;2;3) - eq @(1;2;3)

很明显为什么没有返回 - 左手操作数显然是一个数组,随后的比较(1 -eq @(1;2;3)2 -eq @(1;2;3)等)都不会返回$true

现在谈谈你问题的实际部分。 Active Directory的设计方式使目录中的每个对象都有一个唯一的标识符,您可以使用该标识符来确定它的标识 - objectGUID值。 .NET中的GUID恰好是值类型,因此您可以安全地将其用作比较的基础:

$ones      = Get-ADPrincipalGroupMembership one
$seconds   = Get-ADPrincipalGroupMembership second
$excl_ones = $ones | ? { $_.objectGUID -notin $seconds.objectGUID }

对于安全主体(组,用户,计算机等),另一个可以安全使用的唯一标识符是objectSID - 安全标识符始终是唯一的。