【问题标题】:Powershell hash containskey returns false incorrectlyPowershell 哈希 containskey 错误地返回 false
【发布时间】:2016-04-21 16:20:41
【问题描述】:

我有一个 Powershell 脚本,它获取远程桌面用户会话的列表并按 SessionID 将它们放入哈希中。

# Create a hashtable which contains all of the remote desktop sessions by SessionId
$userSessionBySessionID = @{}
ForEach($userSession in Get-RDUserSession -CollectionName $collectionName)
{
    $userSessionBySessionID.Add($userSession.SessionId, $userSession)
}

然后我可以将 $userSessionByID 转储到 PowerShell ISE 中

Name                           Value                                                                                                                                                                                                          
----                           -----                                                                                                                                                                                                          
9                              Microsoft.RemoteDesktopServices.Management.RDUserSession                                                                                                                                                       
8                              Microsoft.RemoteDesktopServices.Management.RDUserSession                                                                                                                                                       
7                              Microsoft.RemoteDesktopServices.Management.RDUserSession                                                                                                                                                       
6                              Microsoft.RemoteDesktopServices.Management.RDUserSession                                                                                                                                                       
5                              Microsoft.RemoteDesktopServices.Management.RDUserSession                                                                                                                                                       
4                              Microsoft.RemoteDesktopServices.Management.RDUserSession                                                                                                                                                       
2                              Microsoft.RemoteDesktopServices.Management.RDUserSession                                                                                                                                                       
1                              Microsoft.RemoteDesktopServices.Management.RDUserSession       

令人沮丧的部分是 $userSessionBySessionID.ContainsKey(4) 返回 false。我在这里想念什么?我也尝试过 $userSessionBySessionID.ContainsKey("4") ,但这也返回 false。

【问题讨论】:

  • sessionid是什么类型的? $userSession.SessionId.Gettype() 返回什么?
  • 我认为问题可能是 $userSession.SessionId.GetType() 返回 UInt32,而 Get-Process 返回一个 SessionId 为 Int32 的对象。
  • 这是否意味着$userSessionBySessionID.ContainsKey(4 -as [uint32]) 有效?

标签: powershell hash


【解决方案1】:

我认为问题可能在于$userSession.SessionId.GetType() 返回[UInt32]

在测试中,这正是您的问题。考虑以下测试,我使用[UInt32] 创建一个哈希表。

$test = @{}
1..10 | %{$test.add($_ -as [uint32],$_%2)}

如您所见,运行 $test.containskey(6) 返回 false。 $test.containskey("6") 也有同样的问题。然而这返回 true....

$test.containskey(6 -as [uint32])

注意:您不需要在这里使用-as 运算符,因为您可以使用[uint32]6 进行简单的转换,但是如果您使用包含整数的变量-as 会乐于助人。

键本身可以靠近任何对象,containskey() 在所有情况下都返回正确的结果。您的哈希表没有 integer 6 的键。此外,[uint32] cannot be converted to [int] since it has a higher upper bound 因此无法进行强制转换。这将是 PowerShell 或底层 .Net 不会进行“自动”转换的原因。在实践中,我认为这种情况不会发生。

重点是确保您知道类型。

除了这次我们使用整数之外,与上面的示例相同。

1..10 | %{$test.add($_,$_%2)}

$test.containskey(6)
True

$test.containskey("6")
False

当我用字符串创建键时,我可以反转结果。

1..10 | %{$test.add("$_",$_%2)}

$test.containskey(6)
False

$test.containskey("6")
True

【讨论】:

  • 这就是问题所在。在关闭对象的字段时使用 Powershell 哈希时必须非常小心。引用键时,所有内容都应始终转换为单一类型。
  • @bpeikes 只是想确保您能看到我刚刚进行的编辑。你不需要需要使用-as,你可以使用[uint32]6。我只是希望你可能使用变量而不是幻数来展示它。
  • 谢谢。我想出了如何让它工作。我总是在添加哈希和尝试获取值或测试存在时强制转换字段。请参阅下面的答案。
【解决方案2】:

我测试过,发现 $userSessionBySessionID.ContainsKey(4) 会返回 false。但是 $userSessionBySessionID.ContainsKey("4") 返回 true。

我使用的代码如下。请试一试,如果有帮助请告诉我:

    # Create a hashtable which contains all of the remote desktop sessions by SessionId
    $userSessionBySessionID = @{}

    Get-RDUserSession -CollectionName $collectionName | % {$userSessionBySessionID.Add($_.SessionId.ToString().Trim(), $_)}

更多技术信息:

根据内部帮助对 Contains Key 的定义是:

bool ContainsKey(System.Object key)

您可以将字符串解析为此,但不能解析为整数。 您还可以通过运行以下代码(即不带括号)来检查上述定义。它显示方法和任何重载的定义。:

$userSessionBySessionID.ContainsKey

【讨论】:

    【解决方案3】:

    在阅读了其他人的回复后,我弄清楚了问题所在,即当来自 Get-RDUserSession 和 Get-Process 时,SessionId 是不同的类型。代码改为:

    # Create a hashtable which contains all of the remote desktop sessions by SessionId
    $userSessionBySessionID = @{}
    ForEach($userSession in Get-RDUserSession -CollectionName $collectionName)
    {
        $userSessionBySessionID.Add([long]$userSession.SessionId, $userSession)
    }
    
    $userSessionsRunningSurveyor = @()
    ForEach($proc in Get-Process -IncludeUserName)
    {
        $sessionId = [long]$proc.SessionId
        $execPath = $proc.Path
        Write-Debug "Checking for SessionID $sessionId, Path $execPath"
        if($userSessionBySessionID.ContainsKey($sessionId) -and $execPath -and $collectionPaths.ContainsKey($execPath))
        {
            $userName=$proc.UserName
            Write-Debug "Found user $userName running $execPath on SessionID $sessionId"
            $userSession = $userSessionBySessionID[$sessionId]
            $userSessionsRunningSurveyor += $userSession
        }
    }
    

    在添加键和检查键是否存在时,请注意转换为 [long]。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多