为何通过-InputObject参数使用ForEach-Object无法填充强类型字典?
为什么PowerShell中用
-InputObject传递哈希表枚举器给ForEach-Object会失败? 背景
我在脚本中收集用户Java Runtime Environment(JRE)信息,用于验证是否符合要求。首先将JRE变量声明为[List[String]]类型,通过解析java --version的输出来填充:
[List[String]] $Script:JRE = ($(Invoke-Expression -Command 'java --version' | Select-Object -Last 1) ` -replace '(?:[\(\)]|Server VM|build)', '' ` -replace '(?:(\d{2}\-Bit)|([\d\.\-\+]+)|(?:, (.+),) )', "`n`$1`n`$2`n`$3`n" ` -replace ' {2,}', '' ` -replace "`n+", "`n" ` ) | Split-String -Separator "`n" # 这段代码能正常工作,但我总觉得不够优雅
示例结果:
PS> $JRE[0..4] OpenJDK 64-Bit 20.0.1+9-29 mixed mode sharing
随后基于JRE内容做三项检测:
- Java是否存在:
($null -ne $JRE) -and ($JRE[0] -match '(java|OpenJDK)') - 版本是否足够新:
[Version]$JRE[2].Substring(0, $JRE[2].IndexOf('+')) -ge [Version]'20.0.0' - 是否为64位版本:
$JRE[1] -match '64'
检测结果存入强类型字典JavaIs:
[Dictionary[String, Boolean]] $Script:JavaIs = New-Object -TypeName 'Dictionary[String, Boolean]'
测试过程与结果
使用-InputObject参数:❌失败
通过-InputObject传递哈希表枚举器给ForEach-Object时,JavaIs只得到一个空键值对:
ForEach-Object -InputObject @{ Extant = ($null -ne $JRE) -and ($JRE[0] -match '(java|OpenJDK)') Recent = [Version]$JRE[2].Substring(0, $JRE[2].IndexOf('+')) -ge [Version]'20.0.0' x86_64 = $JRE[1] -match '64' }.GetEnumerator() -Process { $JavaIs.Add($_.Key, $_.Value) } # 调换参数顺序结果一样: ForEach-Object -Process { $JavaIs.Add($_.Key, $_.Value) } -InputObject @{ Extant = ($null -ne $JRE) -and ($JRE[0] -match '(java|OpenJDK)') Recent = [Version]$JRE[2].Substring(0, $JRE[2].IndexOf('+')) -ge [Version]'20.0.0' x86_64 = $JRE[1] -match '64' }.GetEnumerator()
结果:
PS> $JavaIs Key Value --- ----- False
使用管道传递:✔成功
通过管道传递哈希表枚举器时,JavaIs能正确填充所有键值对:
@{ Extant = ($null -ne $JRE) -and ($JRE[0] -match '(java|OpenJDK)') Recent = [Version]$JRE[2].Substring(0, $JRE[2].IndexOf('+')) -ge [Version]'20.0.0' x86_64 = $JRE[1] -match '64' }.GetEnumerator() | ForEach-Object -Process { $JavaIs.Add($_.Key, $_.Value) }
结果:
PS> $JavaIs Key Value --- ----- x86_64 True Extant True Recent True
问题原因
核心差异在于-InputObject和管道对集合的处理逻辑不同:
- 管道传递会自动枚举集合类型:哈希表
GetEnumerator()返回的DictionaryEntry集合,管道会逐个将每个DictionaryEntry对象传递给ForEach-Object的Process块,此时$_代表单个键值对,能正常访问Key和Value属性。 -InputObject参数会将整个集合对象作为单个输入项传递:此时$_代表的是整个枚举器集合,而非单个DictionaryEntry。集合本身没有Key和Value属性,所以$_.Key返回$null(显示为空键),$_.Value在布尔上下文里被判定为$false,最终得到一个空键的错误条目。
如果一定要用-InputObject,可以手动在Process块里枚举集合:
ForEach-Object -InputObject @{ Extant = ($null -ne $JRE) -and ($JRE[0] -match '(java|OpenJDK)') Recent = [Version]$JRE[2].Substring(0, $JRE[2].IndexOf('+')) -ge [Version]'20.0.0' x86_64 = $JRE[1] -match '64' }.GetEnumerator() -Process { foreach ($entry in $_) { $JavaIs.Add($entry.Key, $entry.Value) } }
环境信息
| 软件 | 版本 | 架构 |
|---|---|---|
| PowerShell | 7.3.4 | x86_64 |
| Windows | 10.0.19045 | x86_64 |
内容的提问来源于stack exchange,提问作者Magic Spearmint
相关产品推荐
相关产品推荐

