我有一个简单的脚本可以在PC上安装所有应用程序。
$app32 = Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\
$app64 = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
$appAll = $app32 + $app64
$appAll |
ForEach-Object {Get-ItemProperty $_.pspath} |
Select-Object DisplayName,DisplayVersion |
Sort-Object DisplayName |
Export-Csv c:\work\appversion.csv
结果将是垂直的,但我需要制作一个循环来运行PC列表然后输出到csv文件。理想的结果就是这样。
我为此做了很多研究,但找不到办法做到这一点。
我现在所做的是将所有内容输出到文本文件,然后执行select-string
以获取匹配的行。这样我丢失了所有列,整行将在一列中。
请问好吗?或者有更好的方法吗?
答案 0 :(得分:2)
最简单的方法是创建一个自定义对象,然后使用Add-Member将每个软件标题添加为NoteProperty。收集阵列中的所有这些对象(每台计算机一个),然后输出到CSV。您将遇到的问题是您将拥有许多(我的意思是LOTS)软件标题,并且在输出CSV时只考虑第一个条目的属性列表,因此需要额外做一些事情。
你说你有自己的脚本来远离远程机器,但是不要给我们这样做,所以我会使用做的给我们的东西。我过滤了MS更新和修补程序,但您显然可以删除或忽略它。
$app32 = Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\
$app64 = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
$appAll = $app32 + $app64
$AllComputers = @()
ForEach($CompName in (GC C:\ListOfComputers.txt)){
$Computer = [PSCustomObject]@{"PC Name"=$CompName}
$appAll |
ForEach-Object {Get-ItemProperty $_.pspath} |
?{!($_.displayname -match "(?:Update|Hotfix) for Microsoft") -and !([string]::IsNullOrEmpty($_.DisplayName))}|
Select-Object DisplayName,DisplayVersion |
Sort-Object DisplayName|
%{
Add-Member -InputObject $Computer -NotePropertyName $_.DisplayName -NotePropertyValue $_.DisplayVersion
}
$AllComputers+=$Computer
}
#Make sure first computer has properties for all possible software titles
$AllComputers|
%{
$_|GM -MemberType Properties|select -expand Name
}|
Select -Unique|
?{$_ -notin ($AllComputers[0]|gm -MemberType Properties|select -Expand Name)}|
%{
Add-Member -InputObject $AllComputers[0] -NotePropertyName $_ -NotePropertyValue ""
}
#Output to file
$AllComputers|Export-Csv $NewFilePath -NoTypeInformation
编辑:为了尝试解释它,它的作用是为每台具有1个属性的计算机生成PSCustomObject:PC名称。它将该属性的值设置为它当前正在处理的任何计算机。
然后它获取了您已经完成的所有注册表值,并为每个注册表值添加了另一个属性,它为该计算机创建的PSCustomObject。属性名称是软件的标题,值是软件版本。因此,在您假设的第一台PC之后,您将拥有一个看起来像的对象:
PC Name : PC 1
MS Office : 2013
Adobe : 9
7-Zip : 5.2
VNC : 7
PowerShell : 4.5
将该对象添加到数组中,然后为每台计算机进行冲洗和重复。当你完成时,你有一个像你想要的那样输出的数组。您将该数组管道传输到Format-Table,然后得到:
PC Name MS Office Adobe 7-Zip VNC PowerShell
------- --------- ----- ----- --- ----------
PC 1 2013 9 5.2 7 4.5
PC 2 2007 11 5.2 7 4.5
PC 3 2013 11 5.2 7 2
PC 4 2013 9 5.2 7 3
问题是它只显示数组中第一个项目的属性。所以我们假设PC 1没有安装7-Zip。现在,数组中的第一个项目如下所示:
PC Name : PC 1
MS Office : 2013
Adobe : 9
VNC : 7
PowerShell : 4.5
由于当您将数组传输到Format-Table时它没有7-Zip属性,您最终会得到:
PC Name MS Office Adobe VNC PowerShell
------- --------- ----- --- ----------
PC 1 2013 9 7 4.5
PC 2 2007 11 7 4.5
PC 3 2013 11 7 2
PC 4 2013 9 7 3
现在这没什么好处,因为我们想看到所有机器上所有标题的版本号,所以第一条记录需要在任何机器上为每个软件标题都有一个属性,无论它是否安装在第一台电脑。为了实现这一点,我们通过将它传递给ForEach(我使用%别名)循环遍历数组:
$AllComputers|
%{
在ForEach循环中,我们使用当前计算机并执行Get-Member(使用GM别名),并仅选择属性的成员(不包括方法或可能在对象上的事件),并展开名称每个属性。
$_|GM -MemberType Properties|select -expand Name
现在记住我们的属性名称是软件标题和值是每个标题的版本,但我们只想要名称,这样我们就可以得到每台计算机上每个软件标题的列表。所以从那里我们管道选择-Unique去除重复,并继续管道到Where子句(别名?used)来过滤掉第一台计算机已经存在的任何属性:
Select -Unique|
?{$_ -notin ($AllComputers[0]|gm -MemberType Properties|select -Expand Name)}|
这是通过获取第一台计算机的属性名称(第二行中的()中的所有内容的名称),并测试所有计算机中所有属性的大列表中的每个属性是否已经完成在该计算机的属性列表中。它跳过已经存在的那些,并且对于每个新的它进入另一个ForEach循环并执行Add-Member以将每个属性添加到第一台计算机,并且仅使用值“”,因为该计算机没有安装它。
现在第一个条目具有所有可能软件标题的属性,当我们将数组管道传输到FT(Format-Table的缩写)时,我们得到:
PC Name MS Office Adobe VNC PowerShell 7-Zip
------- --------- ----- --- ---------- -----
PC 1 2013 9 7 4.5
PC 2 2007 11 7 4.5 5.2
PC 3 2013 11 7 2 5.2
PC 4 2013 9 7 3 5.2