【问题标题】:(AD) object equality in PowerShell(AD) PowerShell 中的对象相等性
【发布时间】:2018-02-18 17:23:17
【问题描述】:

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

$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 }

我错过了什么吗?

【问题讨论】:

  • 您应该始终比较 AD 对象的 objectGUID
  • 您的$a1 -eq $a2 没有返回值,因为使用左侧数组的运算符充当对数组内容的过滤器。它不是询问数组 1 是否等于数组 2,而是询问 $a1 中等于 $a2 的所有项目。比较@(1,2,1,2) -eq 2
  • @TessellatingHeckler 这实际上是一个我不知道的好功能。但我们当然可以争辩说一致性看起来不同。 @(1,2,1,2) -eq 2 -> @(2,2)2 -eq @(1,2,1,2) -> false
  • @TNT 我会说设计对“所有小于 10 的值”更有意义:@(1,4,12,16) -lt 10 而不是“所有值为 2”,对于文本更是如此,所有行匹配一个正则表达式:@("line1", "line2", "line3") -match 'e[13]' 那么你不会期望反向模式“十大于一个数字数组”或'e[13]' -match @("line1", "line2", "line3") 有同样的意义。

标签: powershell active-directory


【解决方案1】:

要了解您所看到的一些奇怪之处,我们必须退后一步,考虑更大的图景,即框架 PowerShell 建立在:.NET 之上!

.NET 中的对象平等

$user1 -eq $user2 失败,因为$user1$user2两个不同的对象 - 尽管它们可能都表示 Active Directory 中的同一个对象。

当谈到 .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 等)都支持两种不同的模式,具体取决于左侧参数:标量过滤

标量比较

在标量模式下,比较运算符将单个对象作为左侧操作数,将值表达式作为右侧操作数并返回布尔结果:$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 - 安全标识符始终是唯一的。

【讨论】:

  • 哇!谢谢,那真是太棒了(我的意思是实际部分)!我还没有完全理解它,但乍一看 - 我的意思是数组属性 包含 数组内容的各个属性的数组 - 例如,它似乎比 model_ids 的方法更优雅Ruby on Rails 的 ActiveRecord 类。这会让我今晚睡得更好。('h','u','r','r','a' | % { @{yes = $_} }).yes
  • 很棒的解释@Mathias。
  • 我已经编辑了标题并将 identity 替换为 equality 因为,正如您正确命名的那样,这就是我真正在谈论的内容.
  • @TNT 我更新了一些关于@(1;2;3) -eq @(1;2;3) 之谜的答案
猜你喜欢
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多