【问题标题】:Powershell hashtable.containskey is always returning truePowershell hashtable.containskey 总是返回 true
【发布时间】:2020-03-05 15:21:38
【问题描述】:

以下代码试图做一件事。它试图查找自上次运行作业以来已添加到 Active Directory 组的 ID。

它通过从 Active Directory 组中读取用户 ID 并将其与前一天保存在文件中的 ID 进行比较来实现这一点。

我首先将 AD 组读入哈希表 ($ADUsersHashtable) 然后我将文件读入一个类似的哈希表($YesterdaysADUsersFile) 两个哈希表都使用 UserID 作为键。 然后我检查 $ADUsersHashtable 中的每个 ID 是否在 $YesterdaysADUsersFile 中。位于 $ADUsersHashtable 但不在 $YesterdaysADUsersFile 中的 ID 是自上次运行此作业以来添加到 AD 的 ID。

问题是,如果 $YesterdaysADUsersFile 中有多个条目,containskey 方法总是返回 true(见下面的输出)

如果我删除文件中的所有条目,但只有一个条目,代码将按预期工作。

如果我在文件中有多个条目,则代码无法按预期工作。

以下是将 AD 和文件读取到哈希表中然后比较键的代码。

$scriptName = $MyInvocation.MyCommand.Name
$LogFile = "D:\Polarion\data\logs\User Access Dates\$scriptName.log"
$Today = Get-Date
$outDate = get-date -format "yyyy-MM-dd"
Add-content $LogFile "$Today Running $scriptName"
$MyServer = $env:computername
Add-content $LogFile "`t$Today Running on $MyServer"

#Will be populated with IDs from AD that i didn't find yesterday
$NewUsersFile = "D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt" 
clear-content $NewUsersFile #I only want the new users from todays AD group.

$ADUsersHashtable = @{} #Contains IDs of the members in the AD Group.
Get-ADGroupMember -Identity PEALM_ALL_USERS -Recursive `
    | Get-ADObject -Properties SamAccountName, mail `
    | select SamAccountName, mail `
    | foreach {$ADUsersHashtable.Add($_.SamAccountName, $_.mail)}
#$ADUsersHashtable

$YesterdaysADUsersFile = "D:\Polarion\data\logs\User Access Dates\UserIdFromPEALM_ALL_USERS.txt" #Contains the IDs that I knew about the last time this ran.
$YesterdaysADUsersHashTable = @{}
$YesterdaysADUsersHashTable = Get-Content($YesterdaysADUsersFile) | 
    foreach {$_.ToString().Replace(":", "=")} | 
    ConvertFrom-StringData
$YesterdaysADUsersHashTable

$NoNewUsersFound = $true
foreach ($UserIDFromAD in $ADUsersHashtable.keys){ #For each user ID in Todays AD group
    if ($YesterdaysADUsersHashTable.containsKey($UserIDFromAD)){ #If the UserID is in Yesterdays list ignore it.
        write-host YesterdaysADUsersHashTable contains key $UserIDFromAD
    } else {
        $NoNewUsersFound = $false
        write-host YesterdaysADUsersHashTable Doesnt contains key $UserIDFromAD 
        write-host "`tadding $UserIDFromAD to the $NewUsersFile file."
        Add-content $LogFile "`t$Today Adding $UserIDFromAD to $NewUsersFile file"
        Add-Content $NewUsersFile "$UserIDFromAD : $outDate" #if its not in yesterdays list write it to the new users file.
    }
}
if ($NoNewUsersFound){
    Add-content $LogFile "`t$Today No new users IDs found in Active Directory."
}
#Clear-Content $YesterdaysADUsersFile #we want to overwrite the file, not append to it. 
#$ADUsersHashtable.keys `
#    | %{ Add-Content $YesterdaysADUsersFile "$_ : $($ADUsersHashtable.$_)" } #writes the content of the hashtable to a file.

以下是文件(因此 $YesterdaysADUsersHashTable)中有两个条目时的输出。 前四行是 $YesterdaysADUsersHashTable 的转储。 接下来的五行来自 if-containskey 块中 write-host 命令的输出。它们表明 hashtable.containskey 对 $ADUsersHashtable 中的每个键都返回 true。但是这些键不在 $YesterdaysADUsersHashTable 中,这是我不明白的。

Name                           Value                                                                                                                                               
----                           -----                                                                                                                                               
QZMRW2                         xxxx.xxxx@xxxx.com                                                                                                                 
dzrbcn                         xxxx.xxxx@xxxx.com  

YesterdaysADUsersHashTable contains key QZMRW2
YesterdaysADUsersHashTable contains key dzrbcn
YesterdaysADUsersHashTable contains key MZDP2G
YesterdaysADUsersHashTable contains key BZ5LBQ
YesterdaysADUsersHashTable contains key FZ080Y

$YesterdaysADUsersHashTable 显然不包含“MZDP2G”、“BZ5LBQ”或“FZ080Y”

而且,如果我从文件中删除除一个用户 ID 之外的所有内容,代码似乎可以正常工作。 $YesterdaysADUsersHashTable 现在只有一个条目“QZMRW2”并且代码似乎可以工作。

Name                           Value                                                                                                                                               
----                           -----                                                                                                                                               
QZMRW2                         xxxx.xxxx@xxxx.com  

YesterdaysADUsersHashTable contains key QZMRW2
YesterdaysADUsersHashTable Doesnt contains key dzrbcn
    adding dzrbcn to the D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt file.
YesterdaysADUsersHashTable Doesnt contains key MZDP2G
    adding MZDP2G to the D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt file.
YesterdaysADUsersHashTable Doesnt contains key BZ5LBQ
    adding BZ5LBQ to the D:\Polarion\data\logs\User Access Dates\NewUsersInPEALM_ALL_USERSADGroup.txt file.
YesterdaysADUsersHashTable Doesnt contains key FZ080Y

我显然不理解某些东西。

任何建议将不胜感激。

【问题讨论】:

  • 存在一个 key 并不意味着它引用的值不是$null :)
  • @MathiasR.Jessen 可能解决了它 - 重现错误:$hash = @{MyKey = $null}; $hash.MyKey; $hash.MyKey.GetType()。因为哈希表查找键,但返回值,$UserIDsFromPEALM_ALL_USERSFileHashTable.$key.GetType() 试图获取值的类型。我不确定是否有可能获得这样的密钥类型。
  • 如果你想要循环内的键类型,只需执行$key.GetType() :)
  • @MathiasR.Jessen 好的,但我的意思是在我的例子中,当没有循环时。
  • 嗯,可以有多个键 - 如果你只想要第一个:$hash.Keys[0].GetType()

标签: powershell containskey


【解决方案1】:

仅仅因为一个键存在并不意味着对应的值不是$null

示例如下:

$a = @{ keyName = "Value" }
$b = @{ keyName = $null }

foreach($key in $a.Keys){
    if($b.ContainsKey($key)){
        $bValue = $b[$key] # or `$b.$key` if the key is a string

        if($null -eq $bValue){
            Write-Host "Key '$key' exists in '`$b', but its value is `$null :("
        }
    }
}

如果没有关于如何填充哈希表的详细信息,很难说为什么 :)

【讨论】:

【解决方案2】:

我通过将 hashtable.containskey 更改为 -contains 使代码工作。在下面的代码片段中,我注释了包含 .containsKey($UserIDFromAD) 的行并添加了包含 $UserIDsFromFile.keys -contains $UserIDFromAD 的行。代码现在按预期工作。

这与按引用检查和按值检查之间的区别有关。我知道这在 Java 中是如何工作的,但在 Powershell 中却不知道。

#if ($UserIDsFromFile.containsKey($UserIDFromAD)){ #If the UserID is in Yesterdays list ignore it.
if ($UserIDsFromFile.keys -contains $UserIDFromAD){ #If the UserID is in Yesterdays list ignore it.

【讨论】:

  • 您能告诉我们您使用的是哪个版本的 PowerShell 和 .NET?我无法用这个重现它:$innerhash1 = @{Key1 = "innerhash1"}; $innerhash2 = @{Key2 = "innerhash2"}; $outerhash = @{$innerhash1 = "outerhash1"}; $outerhash += @{$innerhash2 = "outerhash2"}; $outerhash - 在此之后,如果我像$outerhash.$innerhash1; $outerhash.Contains($innerhash1); $outerhash.ContainsKey($innerhash1); $outerhash.Keys -contains $innerhash1 一样查询,所有这些都给出了正确的结果。你能测试一下吗?您的结果是否相同 (outerhash1 True True True)?
  • 我发现下面的文章解释了发生了什么。 link
  • 谢谢,但在 .NET 4.7.2 和 PowerShell 5.1 中,情况并非如此:我得到了 True for $hash.ContainsKey($key)
猜你喜欢
  • 2018-05-10
  • 2013-08-06
  • 2017-05-10
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多