PowerShell读取计算机列表导出系统信息时PCName重复写入问题排查
问题分析:PowerShell脚本导出CSV出现重复计算机名称
核心原因
1. 连通性测试逻辑完全失效
脚本中$TstConnect = test-connection $Computer被放在循环外部,此时$Computer尚未从computers.txt读取任何值,导致该变量要么为空,要么保留之前PowerShell会话中的旧计算机值。这会使得后续的在线判断if ($TstConnect.IPV4Address.IPAddressToString[0])完全失效:
- 如果旧值对应的计算机在线,所有循环都会进入在线分支,即使当前遍历的计算机离线;
- 当WMI查询失败时,
$computerSystem、$computerBIOS等变量会保留上一次循环的有效值,最终导致CSV写入重复的旧计算机信息。
2. 多实例WMI查询未处理
部分WMI类会返回多个实例(例如Win32_Processor对应多核心CPU、Win32_LogicalDisk对应多个本地磁盘、Win32_NetworkAdapterConfiguration对应多网卡),直接将集合对象赋值给CSV属性时,PowerShell会自动将数组元素拆分成多行写入,从而出现重复的PCName(其他属性对应不同实例的值)。
修复方案
1. 修正连通性测试位置
将连通性测试移到循环内部,确保每次遍历都针对当前计算机执行测试:
foreach ($Computer in Get-Content c:\myfolder\computers.txt) { $i++ $p = "{0:N1}" -f ($i * 100 / $t) # 移到循环内,每次测试当前计算机 $TstConnect = Test-Connection $Computer -Count 1 -Quiet -ErrorAction SilentlyContinue if ($TstConnect) { # 后续在线分支逻辑... } # 离线分支逻辑... }
2. 处理多实例WMI结果
对可能返回多个实例的WMI查询,通过Select-Object -First 1或索引[0]获取单个实例,避免数组导致的多行写入:
# 取第一个CPU实例 $computerCPU = Get-WmiObject Win32_Processor -Computer $Computer | Select-Object -First 1 # 取第一个本地磁盘 $computerHDD = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter drivetype=3 | Select-Object -First 1 # 取第一个符合条件的以太网网卡 $computerNET = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $Computer | Where-Object { $_.IPAddress -ne $null -and $_.Description -like "*Ethernet*" } | Select-Object -First 1
3. 优化变量作用域
在循环内部初始化关键变量,避免上一次循环的值残留:
foreach ($Computer in Get-Content c:\myfolder\computers.txt) { # 每次循环初始化变量 $computerSystem = $null $computerBIOS = $null $AssetTag = $null # ...其他变量初始化 # 后续逻辑... }
完整修正后的脚本示例
Set-Location "c:\myfolder" $ErrorActionPreference = 'SilentlyContinue' $exportFile = 'c:\myfolder\system-info.csv' $t = 0 $i = 0 $tab_char = [char]9 #Clear screen & old file Clear-Host try { $file = [System.IO.File]::Open($exportfile, 'Open', 'Read', 'None') $file.Close() Write-Host "File $exportfile is not open. Deleting old results file... $exportfile" -BackgroundColor DarkCyan Remove-Item $exportFile } catch { Write-Host "File is open or in use by another process. Ending the task." -BackgroundColor DarkRed break } # Get Totals foreach ($Computer in Get-Content c:\myfolder\computers.txt) { $t++ } Write-Host "$t total computers " -BackgroundColor DarkCyan foreach ($Computer in Get-Content c:\myfolder\computers.txt) { $i++ $p = "{0:N1}" -f ($i * 100 / $t) # 移到循环内的连通性测试 $TstConnect = Test-Connection $Computer -Count 1 -Quiet -ErrorAction SilentlyContinue # 初始化变量,避免残留 $computerSystem = $null $computerBIOS = $null $AssetTag = $null $computerOS = $null $computerCPU = $null $computerNET = $null $computerIP = $null $computerHDD = $null $csvObject = $null if ($TstConnect) { $computerSystem = Get-WmiObject Win32_ComputerSystem -Computer $Computer if ($computerSystem) { Write-Host $computerSystem.name "is online, checking $tab_char $i of $t $tab_char $p% complete " -BackgroundColor DarkCyan $computerBIOS = Get-WmiObject Win32_BIOS -Computer $Computer $AssetTag = Get-WmiObject -Class Win32_SystemEnclosure -Computer $Computer | Select-Object SMBiosAssetTag $computerOS = Get-WmiObject Win32_OperatingSystem -Computer $Computer # 取第一个CPU实例 $computerCPU = Get-WmiObject Win32_Processor -Computer $Computer | Select-Object -First 1 # 取第一个符合条件的网卡 $computerNET = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $Computer | Where-Object { $_.IPAddress -ne $null -and $_.Description -like "*Ethernet*" } | Select-Object -First 1 $computerIP = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName $computer | Select-Object -Property ipaddress # 取第一个本地磁盘 $computerHDD = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter drivetype=3 | Select-Object -First 1 $csvObject = New-Object PSObject -Property @{ 'PCName' = $computerSystem.Name 'MACAddress' = $computerNET.MACAddress 'IPAddress' = ($computerIP.ipaddress)[0] 'Manuf' = $computerSystem.Manufacturer 'Model' = $computerSystem.Model 'AssetTag' = $AssetTag.SMBiosAssetTag 'SerialNumber' = $computerBIOS.SerialNumber 'RAM' = "{0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB) 'HDDSize' = if ($computerHDD) { "{0:N2}" -f ($computerHDD.Size/1GB) } else { "N/A" } 'HDDFree' = if ($computerHDD) { "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size) } else { "N/A" } 'CPU' = $computerCPU.Name 'OS' = $computerOS.caption 'Version' = $computerOS.Version 'SP' = $computerOS.ServicePackMajorVersion 'User' = $computerSystem.UserName 'BootTime' = $computerOS.ConvertToDateTime($computerOS.LastBootUpTime) } } else { # 处理WMI查询失败的情况 Write-Host "$Computer is online but WMI access failed... $p% complete" -BackgroundColor DarkYellow $csvObject = New-Object PSObject -Property @{ 'PCName' = $Computer 'Manuf' = "WMI Access Failed" 'Model' = "WMI Access Failed" 'SerialNumber' = "WMI Access Failed" 'AssetTag' = "WMI Access Failed" 'RAM' = "WMI Access Failed" 'HDDSize' = "WMI Access Failed" 'HDDFree' = "WMI Access Failed" 'CPU' = "WMI Access Failed" 'OS' = "WMI Access Failed" 'Version' = "WMI Access Failed" 'SP' = "WMI Access Failed" 'User' = "WMI Access Failed" 'BootTime' = "WMI Access Failed" 'MACAddress' = "WMI Access Failed" 'IPAddress' = "WMI Access Failed" } } } else { Write-Host "$Computer is not online... $p% complete" -BackgroundColor DarkRed $csvObject = New-Object PSObject -Property @{ 'PCName' = $Computer 'Manuf' = "xxxxx" 'Model' = "xxx Offline xxx" 'SerialNumber' = "xxxxx" 'AssetTag' = "xxxxx" 'RAM' = "xxxxx" 'HDDSize' = "xxxxx" 'HDDFree' = "xxxxx" 'CPU' = "xxxxx" 'OS' = "xxxxx" 'Version' = "xxxxx" 'SP' = "xxxxx" 'User' = "xxxxx" 'BootTime' = "xxxxx" 'MACAddress' = "xxxxx" 'IPAddress' = "xxxxx" } } if ($csvObject) { $csvObject | Select PCName, HDDFree, Manuf, Model, OS, Version, SP, User, BootTime, MACAddress, IPAddress, AssetTag | Export-Csv $exportfile -NoTypeInformation -Append } } Write-Host "All computers processed. File created at $exportfile" do { $response = Read-Host -Prompt "Do you want to open the file to view it? (Y/N)" if ($response -eq "Y") { Write-Host "Opening $exportfile..." Start-Process -FilePath $exportfile break } elseif ($response -eq "N") { Write-Host "Exiting..." break } else { Write-Host "Invalid input. Please enter Y or N." } } while ($true)
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

