多远程系统已安装软件导出至CSV脚本问题排查
嘿,我帮你找出了脚本里的几个核心问题,这才导致它只导出本地数据。咱们一步步来修正,让它能正常抓取远程机器的软件信息:
原脚本的问题分析
- 没有使用远程计算机名:你在
Process块里通过管道拿到的远程机器名是$_,但全程都用了$env:COMPUTERNAME(本地机器名),所以不管txt里写了啥,脚本都只会读取本地数据。 - 没有远程访问注册表:
Get-ItemProperty默认只读取本地注册表,要获取远程机器的软件信息,必须用远程执行的方式。 - CSV导出会覆盖:每次循环都直接执行
Export-CSV,会把之前的结果覆盖掉,最后只能看到最后一次(本地)的数据。 - 路径硬编码不灵活:输入输出路径都是硬编码的,不方便其他用户修改。
修正后的脚本
Function Export-InstalledSoftware { [CmdletBinding()] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [string[]]$ComputerName, [string]$OutputPath = "C:\Users\$env:USERNAME\Desktop\SoftwareQuery_$(Get-Date -f yyyy-MM-dd).csv" ) Begin { # 初始化数组存储所有结果,避免多次导出覆盖 $allResults = @() } Process { foreach ($computer in $ComputerName) { Write-Host "Processing: $computer" # 测试远程机器连通性 if (Test-Connection -ComputerName $computer -Count 1 -Quiet) { try { # 远程执行注册表读取操作,获取软件信息 $remoteResults = Invoke-Command -ComputerName $computer -ScriptBlock { Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*,HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* ` | Where-Object { $_.DisplayName -ne $null } # 过滤空名称的无效条目 | Select-Object @{n='ComputerName';e={$env:COMPUTERNAME}},DisplayName,Publisher,DisplayVersion } -ErrorAction Stop # 将远程结果加入总数组 $allResults += $remoteResults } catch { Write-Warning "Failed to get data from $computer : $_" } } else { Write-Warning "Can't ping $computer, skipping this machine..." } } } End { # 统一排序并导出所有结果到CSV $allResults | Sort-Object DisplayName | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding ASCII Write-Host "Done! Export file saved to: $OutputPath" } } # 自定义输入文件路径,用户可以修改这里 $inputFilePath = "C:\Users\$env:USERNAME\Desktop\systems.txt" Get-Content -Path $inputFilePath | Export-InstalledSoftware
关键改动说明
- 参数化路径:把输入文件路径做成变量,输出路径做成可选参数,方便不同用户直接修改,不用动函数内部的代码。
- 正确使用远程计算机名:循环里用
$computer(来自txt文件的机器名)代替$env:COMPUTERNAME,远程脚本块里的$env:COMPUTERNAME会自动取远程机器的名称,确保CSV里的ComputerName列正确。 - 远程执行命令:用
Invoke-Command在远程机器上执行注册表读取操作,这样才能拿到真实的远程软件数据(需要目标机器开启WinRM服务,且你有管理员权限)。 - 避免CSV覆盖:先把所有结果存到
$allResults数组里,最后统一导出,不会出现覆盖问题。 - 错误处理:添加
try/catch捕获远程执行的错误,用Write-Warning提示,不会因为某一台机器失败导致整个脚本停止。 - 过滤无效数据:用
Where-Object过滤掉DisplayName为空的条目,避免导出没用的行。
注意事项
- 要确保远程机器开启了WinRM服务:可以在目标机器上以管理员身份执行
Enable-PSRemoting -Force来开启。 - 你需要有远程机器的管理员权限,才能读取它的注册表信息。
- 如果不想用
Invoke-Command,也可以尝试用Get-CimInstance Win32_Product,但这个命令会触发MSI验证,可能影响远程机器性能,而且有些软件不会被收录,所以远程注册表的方式更可靠。
内容的提问来源于stack exchange,提问作者Bill Hart
相关产品推荐
相关产品推荐

