我有一个脚本,它获取服务器列表,浏览其注册表并使用通配符查找注册表值。对于每个主机名,它都会生成对象,但其属性各不相同。这些对象被添加到集合中,然后我将此集合导出为 csv。我希望在那里有所有可能的列标题,但似乎我总是只从集合的第一个对象中获取这些标题。如果我事先不知道所有可能的对象属性,有什么更好的方法吗?
$res=@()
$hosts = Get-Content hosts.txt
foreach ($hostname in $hosts){
$server = New-Object -TypeName psobject
$server | Add-Member -MemberType NoteProperty -Name hostname -Value $hostname
$server | Add-Member -MemberType NoteProperty -Name online -Value ""
$server | Add-Member -MemberType NoteProperty -Name IP -Value ""
$server | Add-Member -MemberType NoteProperty -Name OS -value ""
$rtn=test-connection $hostname -Count 2 -BufferSize 16 -erroraction silentlycontinue
if(!$rtn){
$server.online=$false
write-host "$hostname offline" -ForegroundColor red;
}else{
$server.online=$true
$server.IP=(($rtn.properties|? name -eq "ProtocolAddress").value|select -first 1)
$wmi=get-wmiobject -computer $hostname win32_operatingsystem -ErrorAction SilentlyContinue
$server.OS=$wmi.caption
$s=Invoke-Command -cn $hostname -ScriptBlock {
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*|where {$_.displayname -like "*McAfee*"}|select displayname,displayversion
}
$s|foreach{
$server | Add-Member -MemberType NoteProperty -Name $_.displayname -Value $_.DisplayVersion
}
}
$res+=$server
}
$res
$res|export-csv "versions.csv" -NoTypeInformation
notepad "versions.csv"
只需使用几个主机名进行测试,然后在注册表中搜索某些内容,最好是在不同的操作系统中,并将其中一些无法访问的主机名也放在那里。
示例输出如下:
hostname : server1
online : True
IP : secretip
OS : Microsoft Windows Server 2008 R2 Standard
McAfee Agent : 5.0.5.658
hostname : server2
online : True
IP : secretip
OS : Microsoft Windows Server 2016 Standard
McAfee Agent : 5.0.5.658
McAfee Endpoint Security Threat Prevention : 10.5.1
McAfee_EndpointSecurityForServer_10.5.1_0_x64_P0_EN : 10.5.1
hostname : server3
online : True
IP : secretip
OS : Microsoft Windows Server 2008 R2 Standard
McAfee Agent : 5.0.5.658
hostname : server4
online : True
IP : secretip
OS : Microsoft Windows Server 2008 R2 Standard
McAfee Agent : 5.0.5.658
hostname : server5
online : False
IP :
OS :
hostname : server6
online : True
IP : secretip
OS : Microsoft Windows Server 2012 R2 Standard
McAfee Agent : 5.0.5.658
McAfee Endpoint Security Threat Prevention : 10.5.1
McAfee_EndpointSecurityForServer_10.5.1_0_x64_P0_EN : 10.5.1
hostname : server7
online : True
IP : secretip
OS : Microsoft Windows Server 2016 Standard
McAfee Agent : 5.0.5.658
McAfee Endpoint Security Threat Prevention : 10.5.1
McAfee Endpoint Security Platform : 10.5.1
上面得到的 CSV 只包含集合中第一个对象的列,但我想要这些字符串的所有可能变体 McAfee
答案1
我建议:
- 将每个服务器的数据保存到单独的 csv 文件中,然后
- 首先收集列数据,然后使用 Select-Object 将其应用于每个 csv,
- 在没有该列的文件上创建空列,并对所有列进行同等排序
因此,
$res+=$server
插入
$server | Export-Csv "X:\path\$($server.Hostname).csv" -NoTypeInformation
并让此脚本进行统一:
## Q:\Test\2019\01\18\sf_949737.ps1
$Columns = @('hostname','online','IP','OS')
$Files = (Get-ChildItem server*.csv)
## gather all columns/header
ForEach($File in $Files){
((Import-Csv $File)[0].psobject.Properties).Name | ForEach-Object{
if($Columns -notcontains $_){$Columns += $_}
}
}
"All columns`r`n-----------"
$Columns
"="*75
@() | Select-Object $Columns | Export-Csv Versions.csv -NoTypeInformation
ForEach($File in $Files){
Import-Csv $File | Select-Object $Columns | Export-Csv Versions.csv -Append -NoTypeInformation
}
Import-Csv Versions.csv
根据以上数据的示例输出:
> Q:\Test\2019\01\18\sf_949737.ps1
All columns
-----------
hostname
online
IP
OS
McAfee Agent
McAfee_EndpointSecurityForServer_10.5.1_0_x64_P0_EN
McAfee Endpoint Security Threat Prevention
McAfee Endpoint Security Platform
===========================================================================
hostname : server1
online : True
IP : secretip
OS : Microsoft Windows Server 2008 R2 Standard
McAfee Agent : 5.0.5.658
McAfee_EndpointSecurityForServer_10.5.1_0_x64_P0_EN :
McAfee Endpoint Security Threat Prevention :
McAfee Endpoint Security Platform :
hostname : server2
online : True
IP : secretip
OS : Microsoft Windows Server 2016 Standard
McAfee Agent : 5.0.5.658
McAfee_EndpointSecurityForServer_10.5.1_0_x64_P0_EN : 10.5.1
McAfee Endpoint Security Threat Prevention : 10.5.1
McAfee Endpoint Security Platform :
...
答案2
谢谢你的解决方案,很有用。同时,我还找到了一个更短的解决方案,无需使用其他 CSV 文件:
$firstrow=$res|select -first 1
$initObj=$firstrow
$res|foreach{
foreach($object_properties in $_.PsObject.Properties){
if(!(Get-Member -inputobject $initobj -name $object_properties.Name -Membertype Properties)){
write-host "adding property "$object_properties.Name
$initObj | Add-Member -MemberType NoteProperty -Name $object_properties.Name -Value $firstrow.($object_properties.Name)
}
}
}
$resfinal=@()
$resfinal+=$initobj
foreach ($row in ($res| select -skip 1)){
$resfinal+=$row
}
这将使用第一行或 $res 中的值创建新对象 $initObj,然后遍历之前创建的 $res 数组中的所有对象并检查它们的属性名称是否在 $initobj 中。如果不在,则添加它们。然后我创建新数组 $resfinal,添加 $initobj 作为第一个元素,然后添加 $res 中的所有其他元素。