如何用PowerShell对比两个CSV文件并生成带软件状态的新CSV文件
PowerShell 实现软件清单对比与状态标记脚本
前提约定:
- 主清单文件
SoftwareMaster.csv、当前导出的已安装清单CurrentInstalled.csv均包含softwarename、softwareversion两个核心匹配字段,其余字段与你给出的示例结构一致- 匹配规则为「软件名+版本号」唯一标识一个应用,避免重名误判
完整可运行脚本
# 1. 定义配置项,替换为你本地的实际路径 $hostName = "den00ltm" $masterCsvPath = "C:\path\to\SoftwareMaster.csv" $currentCsvPath = "C:\path\to\CurrentInstalled.csv" $outputCsvPath = "C:\path\to\FinalSoftwareList.csv" # 2. 加载两个CSV文件 $masterList = Import-Csv -Path $masterCsvPath -Encoding UTF8 $currentList = Import-Csv -Path $currentCsvPath -Encoding UTF8 # 3. 解析已卸载应用列表为匹配用哈希表(key=软件名|版本号) $removedAppRaw = @( 'Application Verifier x64 External Package 10.1.17763.132' 'C++ | Visual Fortran Compiler 17.0 for IA-32 17.0.9.279' 'C++ | Visual Fortran Compiler 17.0 for IA-32 17.0.8.275' 'C++ | Visual Fortran Compiler 17.0 common files 17.0.5.267' 'Build Tools - x86 12.0.40675' ) $removedMap = @{} foreach ($raw in $removedAppRaw) { # 正则拆分多空格分隔的软件名和版本号 if ($raw -match '^\s*(.+?)\s{2,}(\S+)\s*$') { $name = $matches[1].Trim() $version = $matches[2].Trim() $key = "$name|$version" $removedMap[$key] = $true } } # 4. 解析新安装应用列表为匹配哈希表 $newAppRaw = @( 'C++ OpenMP on Intel(R) 64 12.0.5.229' ) $newMap = @{} foreach ($raw in $newAppRaw) { if ($raw -match '^\s*(.+?)\s{2,}(\S+)\s*$') { $name = $matches[1].Trim() $version = $matches[2].Trim() $key = "$name|$version" $newMap[$key] = [PSCustomObject]@{ softwarename = $name softwareversion = $version } } } # 5. 构建当前已安装应用的匹配哈希表 $currentMap = @{} foreach ($app in $currentList) { $key = "$($app.softwarename.Trim())|$($app.softwareversion.Trim())" $currentMap[$key] = $app } # 6. 处理主清单所有条目,添加状态字段 $result = @() foreach ($masterApp in $masterList) { $key = "$($masterApp.softwarename.Trim())|$($masterApp.softwareversion.Trim())" # 复制主条目所有原有属性 $outputApp = $masterApp | Select-Object * # 匹配状态 if ($currentMap.ContainsKey($key)) { $outputApp | Add-Member -MemberType NoteProperty -Name "Softwarestatus" -Value "Active" -Force # 用当前清单的最新字段覆盖主清单旧字段(如安装路径、安装时间有变更的场景) foreach ($prop in $currentMap[$key].PSObject.Properties.Name) { if ($outputApp.PSObject.Properties.Name -contains $prop -and $currentMap[$key].$prop) { $outputApp.$prop = $currentMap[$key].$prop } } } elseif ($removedMap.ContainsKey($key)) { $outputApp | Add-Member -MemberType NoteProperty -Name "Softwarestatus" -Value "removed" -Force } else { # 不在当前列表也不在已卸载列表的应用,可自行调整状态规则 $outputApp | Add-Member -MemberType NoteProperty -Name "Softwarestatus" -Value "Active" -Force } $result += $outputApp } # 7. 添加新安装的、不在主清单中的条目 foreach ($newKey in $newMap.Keys) { $isExist = $masterList.Where({ $_.softwarename.Trim() -eq $newMap[$newKey].softwarename -and $_.softwareversion.Trim() -eq $newMap[$newKey].softwareversion }, 'First').Count -gt 0 if (-not $isExist) { # 从当前已安装清单取完整字段 $newApp = $currentMap[$newKey] | Select-Object * $newApp | Add-Member -MemberType NoteProperty -Name "Softwarestatus" -Value "newly added" -Force # 补全主清单所有字段,无值的留空 foreach ($prop in $masterList[0].PSObject.Properties.Name) { if (-not ($newApp.PSObject.Properties.Name -contains $prop)) { $newApp | Add-Member -MemberType NoteProperty -Name $prop -Value "" -Force } } $result += $newApp } } # 8. 导出最终结果CSV $result | Export-Csv -Path $outputCsvPath -Encoding UTF8 -NoTypeInformation
注意事项
- 如果你的CSV文件中软件名、版本号的字段名和示例不一致,需要同步修改脚本中对应字段名的引用
- 若不需要保留主清单中既不在当前安装列表、也不在已卸载列表的应用,直接在第6步对应分支过滤即可
内容的提问来源于stack exchange,提问作者Chandra Sekhar
相关产品推荐
相关产品推荐

