适用于 PowerShell 5.1 及以上版本
哈希表(Hashtable)是 PowerShell 中使用频率最高的数据结构之一——从 ConvertFrom-Json 的输出到 Invoke-Command 的参数,从配置管理到缓存系统,哈希表无处不在。然而,很多用户只会基本的键值操作,不了解有序字典、大小写敏感性、嵌套结构、线程安全字典等高级特性。深入理解这些特性,可以写出更高效、更优雅的代码。
本文将系统讲解 PowerShell 中哈希表和相关字典类型的用法与最佳实践。
基础操作与技巧 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 $config = @ { Server = "prod-db01" Port = 5432 Database = "MyApp" Timeout = 30 ReadOnly = $false } Write-Host "服务器:$ ($config .Server)" Write-Host "端口:$ ($config ['Port'])" $key = "Database" Write-Host "数据库:$ ($config [$key ])" $config ["MaxPoolSize" ] = 100 $config .Remove("ReadOnly" )if ($config .ContainsKey("Timeout" )) { Write-Host "超时设置:$ ($config .Timeout) 秒" } $config .GetEnumerator() | ForEach-Object { Write-Host " $ ($_ .Key) = $ ($_ .Value)" } $config2 = $config .Clone()$config2 ["Port" ] = 3306 Write-Host "原端口:$ ($config .Port),克隆端口:$ ($config2 .Port)" $defaults = @ { Timeout = 30 ; Retry = 3 ; Verbose = $false }$override = @ { Timeout = 60 ; Server = "dev-db01" }$merged = @ { }foreach ($key in $defaults .Keys) { $merged [$key ] = $defaults [$key ] }foreach ($key in $override .Keys) { $merged [$key ] = $override [$key ] }Write-Host "合并后 Timeout:$ ($merged .Timeout),Server:$ ($merged .Server)"
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 服务器:prod-db01 端口:5432 数据库:MyApp 超时设置:30 秒 Server = prod-db01 Port = 5432 Database = MyApp Timeout = 30 MaxPoolSize = 100 原端口:5432 ,克隆端口:3306 合并后 Timeout:60 ,Server:dev-db01
有序字典 普通哈希表不保证键的顺序。如果需要保持插入顺序,使用 [ordered]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 $unordered = @ { Z = 1 ; A = 2 ; M = 3 }Write-Host "普通哈希表键序:$ ($unordered .Keys -join ', ')" $ordered = [ordered ]@ { First = "Step1" ; Second = "Step2" ; Third = "Step3" }Write-Host "有序字典键序:$ ($ordered .Keys -join ', ')" $deploySteps = [ordered ]@ { "1-备份" = { Write-Host " 备份数据库..." } "2-停止" = { Write-Host " 停止服务..." } "3-更新" = { Write-Host " 更新文件..." } "4-迁移" = { Write-Host " 运行迁移..." } "5-启动" = { Write-Host " 启动服务..." } "6-验证" = { Write-Host " 健康检查..." } } Write-Host "执行部署步骤:" -ForegroundColor Cyanforeach ($step in $deploySteps .GetEnumerator()) { Write-Host "[$ ($step .Key)]" -ForegroundColor Yellow -NoNewline & $step .Value } $obj = [PSCustomObject ]$ordered $obj | Format-Table
执行结果示例:
1 2 3 4 5 6 7 8 9 普通哈希表键序:Z , A , M 有序字典键序:First , Second , Third 执行部署步骤: [1 -备份] 备份数据库... [2 -停止] 停止服务... [3 -更新] 更新文件... [4 -迁移] 运行迁移... [5 -启动] 启动服务... [6 -验证] 健康检查...
嵌套哈希表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 $envConfig = @ { dev = @ { Db = @ { Server = "dev-db" ; Port = 5432 } Api = @ { Url = "https://dev-api.example.com" ; Key = "dev-key" } Log = @ { Level = "Debug" ; Path = "C:\Logs\dev" } } staging = @ { Db = @ { Server = "staging-db" ; Port = 5432 } Api = @ { Url = "https://staging-api.example.com" ; Key = "staging-key" } Log = @ { Level = "Info" ; Path = "C:\Logs\staging" } } prod = @ { Db = @ { Server = "prod-db" ; Port = 5432 } Api = @ { Url = "https://api.example.com" ; Key = $null } Log = @ { Level = "Warning" ; Path = "C:\Logs\prod" } } } function Get-NestedValue { param ($Hashtable , [string []]$Path ) $current = $Hashtable foreach ($key in $Path ) { if ($current -is [hashtable ] -and $current .ContainsKey($key )) { $current = $current [$key ] } else { return $null } } return $current } $dbServer = Get-NestedValue $envConfig "dev" , "Db" , "Server" Write-Host "开发环境数据库:$dbServer " function Merge-HashtableDeep { param ($Base , $Override ) $result = @ {} foreach ($key in $Base .Keys) { $result [$key ] = $Base [$key ] } foreach ($key in $Override .Keys) { if ($result [$key ] -is [hashtable ] -and $Override [$key ] -is [hashtable ]) { $result [$key ] = Merge-HashtableDeep $result [$key ] $Override [$key ] } else { $result [$key ] = $Override [$key ] } } return $result } $customOverride = @ { prod = @ { Db = @ { Port = 5433 }; Log = @ { Level = "Error" } } }$customConfig = Merge-HashtableDeep $envConfig $customOverride Write-Host "自定义生产环境端口:$ (Get-NestedValue $customConfig 'prod','Db','Port')" Write-Host "自定义生产环境日志级别:$ (Get-NestedValue $customConfig 'prod','Log','Level')"
执行结果示例:
1 2 3 开发环境数据库:dev-db 自定义生产环境端口:5433 自定义生产环境日志级别:Error
从对象和 JSON 转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 function ConvertTo-Hashtable { param ( [Parameter (Mandatory )][object ]$InputObject , [int ]$Depth = 10 ) if ($Depth -le 0 ) { return $InputObject } if ($InputObject -is [System.Collections.IEnumerable ] -and $InputObject -isnot [string ]) { return @ ($InputObject | ForEach-Object { ConvertTo-Hashtable $_ ($Depth - 1 ) }) } if ($InputObject -is [System.Management.Automation.PSCustomObject ]) { $hash = @ {} $InputObject .PSObject.Properties | ForEach-Object { $hash [$_ .Name ] = ConvertTo-Hashtable $_ .Value ($Depth - 1 ) } return $hash } return $InputObject } $json = '{"name":"MyApp","version":"1.2.0","features":["auth","api","web"]}' $obj = $json | ConvertFrom-Json $hash = ConvertTo-Hashtable $obj Write-Host "应用名:$ ($hash .name),版本:$ ($hash .version)" Write-Host "特性:$ ($hash .features -join ', ')" $metrics = @ { "CPU使用率" = 75.2 "内存使用率" = 82.1 "磁盘使用率" = 45.6 "网络延迟" = 12.3 "错误率" = 0.5 } $alerts = $metrics .GetEnumerator() | Where-Object { $_ .Value -gt 50 }Write-Host "超过阈值的指标:" -ForegroundColor Yellow$alerts | ForEach-Object { Write-Host " $ ($_ .Key):$ ($_ .Value)" -ForegroundColor Red }
执行结果示例:
1 2 3 4 5 应用名:MyApp,版本:1.2 .0 特性:auth, api, web 超过阈值的指标: CPU 使用率:75.2 内存使用率:82.1
线程安全字典 在并发场景中使用 ConcurrentDictionary:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 $results = [System.Collections.Concurrent.ConcurrentDictionary [string , object ]]::new()$computers = @ ("SRV01" , "SRV02" , "SRV03" , "SRV04" , "SRV05" )$computers | ForEach-Object -Parallel { $dict = $using:results $computer = $_ try { $ping = Test-Connection -ComputerName $computer -Count 1 -Quiet $os = if ($ping ) { (Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop).Caption } else { "不可达" } $dict .TryAdd($computer , @ { Online = $ping OS = $os Checked = (Get-Date -Format 'HH:mm:ss' ) }) | Out-Null } catch { $dict .TryAdd($computer , @ { Online = $false ; OS = "错误" ; Error = $_ .Exception.Message }) | Out-Null } } $results .GetEnumerator() | Sort-Object Name | ForEach-Object { $status = if ($_ .Value.Online) { "在线" } else { "离线" } $color = if ($_ .Value.Online) { "Green" } else { "Red" } Write-Host "$ ($_ .Key): $status - $ ($_ .Value.OS)" -ForegroundColor $color }
执行结果示例:
1 2 3 4 5 SRV01: 在线 - Microsoft Windows Server 2022 Standard SRV02: 在线 - Microsoft Windows Server 2022 Standard SRV03: 离线 - 不可达 SRV04: 在线 - Microsoft Windows Server 2019 Standard SRV05: 在线 - Microsoft Windows Server 2022 Datacenter
注意事项
键的大小写 :[hashtable] 默认大小写不敏感,[Dictionary[string,object]] 默认大小写敏感
遍历时修改 :遍历 GetEnumerator() 时不能添加或删除键,应先收集要修改的键再操作
浅拷贝陷阱 :Clone() 只复制第一层引用,嵌套对象仍共享。深度拷贝需要递归处理
有序字典类型 :[ordered] 创建的是 OrderedDictionary,不是 Hashtable,两者方法略有不同
性能 :大量数据查找用哈希表(O(1)),不要用数组遍历(O(n))
JSON 转换 :ConvertFrom-Json 在 PowerShell 7 中返回有序字典,在 5.1 中返回 PSCustomObject