【问题标题】:Remove column name from PowerShell select-object results从 PowerShell 选择对象结果中删除列名
【发布时间】:2017-01-06 22:49:22
【问题描述】:

总的来说,我的目标是获取远程计算机列表的 VNC 版本以及卸载 GUID,以便我可以从某些计算机远程卸载 VNC 查看器。我使用了 Get-WmiObject -Class Win32_Product,但这非常慢。

我有以下脚本,但在结果中它包含选择对象参数的名称。

$computers = Get-Content -Path "C:\Computers.txt"

$Results = @()

ForEach ($Computer in $Computers) {
    $Results += New-Object PSObject -Property @{
        "ComputerName" = $Computer

        "Name" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
        | Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object DisplayName

        "DisplayVersion" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
        | Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object DisplayVersion

        "ModifyPath" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
        | Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object ModifyPath

        "Vendor" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
        | Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object Publisher

    }
}

$Results | Select-Object ComputerName,Name,DisplayVersion,ModifyPath,Vendor | Sort-Object ComputerName  | Export-Csv  C:\VNC.csv -notype ;

我的结果如下所示:

计算机名   : 计算机名
名称                  :@{DisplayName=VNC Viewer 5.2.3}
DisplayVersion     :@{DisplayVersion=5.2.3}
ModifyPath          :@{ModifyPath=MsiExec.exe /I{18B1E36F-0DA3-4FDA-BC57-DD815B0DF3B2}}
供应商                 :@{Publisher=RealVNC Ltd}

我希望它看起来像这样:

计算机名   : 计算机名
名称                  :VNC 查看器 5.2.3
显示版本     :5.2.3
ModifyPath          :MsiExec.exe /I{18B1E36F-0DA3-4FDA-BC57-DD815B0DF3B2}
供应商                 :RealVNC Ltd

这是可能的还是我完全错误地处理这个脚本?我还没有想出一种方法来为多个参数运行这个 Invoke-Command 并且仍然以任何其他方式将结果输出到各个列中。

此脚本有效,但需要 100 台计算机才能运行:

if (Test-Path C:\VNCInstalled.csv) {Remove-Item C:\VNCInstalled.csv}
if (Test-Path C:\Computers.txt) {Remove-Item C:\Computers.txt}
$DirSearcher = New-Object System.DirectoryServices.DirectorySearcher([adsi]'')
$DirSearcher.Filter = '(&(objectClass=Computer)(!(cn=*esx*)) (!(cn=*slng*)) (!(cn=*dcen*)) )'
$DirSearcher.FindAll().GetEnumerator() | sort-object { $_.Properties.name } `
| ForEach-Object { $_.Properties.name }`
| Out-File -FilePath C:\Computers.txt

Get-Content -Path c:\Computers.txt `
| ForEach-Object {Get-WmiObject -Class Win32_Product -ComputerName $_} `
| Where-Object -FilterScript {$_.Name -like "VNC V*"} `
| select-object @{Name="ComputerName";Expression={$_.PSComputerName}},
                Name,
                @{Name="InstallLocation";Expression={$_.PackageCache}},
                Vendor,
                Version,
                @{Name="GUID";Expression={$_.IdentifyingNumber}} `
| Sort-Object ComputerName `
| Export-CSV -path c:\VNCInstalled.csv -notype

【问题讨论】:

  • 试试select-object -ExpandProperty DisplayVersion
  • 哇,这么简单,太棒了,谢谢!!

标签: powershell uninstallation


【解决方案1】:

将所有Select-Object 命令更改为Select-Object -ExpandProperty PropertyName,以丢弃属性名称/列标题。

这是我三年前给出的答案,我认为这确实是一个糟糕的答案。让我现在做得更好。

为什么您当前的代码很慢

您当前的代码会枚举 AD 中的所有机器并将它们放入名为 Computers.txt 的文件中。很简单,你做的很好。

接下来,您的代码将执行此操作:

Get-Content -Path c:\Computers.txt | 
ForEach-Object {Get-WmiObject -Class Win32_Product -ComputerName $_} `
| Where-Object -FilterScript {$_.Name -like "VNC V*"} [...]

这可以概括为“对于每台计算机,请求完整的 Win32_product 表,然后过滤到名为 VNC 的应用程序。”这非常会影响性能,原因有很多。

  1. 即使在快速的现代计算机上,查询 Win32_Product 也需要 30 秒或更长时间,因为它会返回每个已安装的应用程序。 (对我来说,在一个新的虚拟机上只安装了几个应用程序就花了一分钟多的时间!)

  2. 查询 Win32_Product 也有这个有趣的怪癖,这使得它需要更长的时间,引用自 Win32_Product 类的 MSDN 文档

警告 Win32_Product 未经过查询优化。诸如“select * from Win32_Product where (name like 'Sniffer%')”之类的查询要求 WMI 使用 MSI 提供程序来枚举所有已安装的产品,然后依次解析完整列表以处理“where”子句。 此过程还会启动已安装软件包的一致性检查,验证和修复安装。 使用只有用户权限的帐户,因为该用户帐户可能无法访问很多位置,可能会导致延迟应用程序启动和事件 11708 说明安装失败。 For more information, see KB Article 794524.

总而言之,查询 Win32_Product 很慢,而且它还会触发对每个应用程序的一致性检查,并且我们还编写了此查询以在过滤之前检索每个应用程序。这些加起来是一个过程,每台电脑可能需要大约 3 分钟,并且将连续运行(一个接一个)并且需要永远。

如何解决

可以在两个地方可靠地检索软件信息:

  • 如果您的设备上安装了 SCCM/ConfigMgr,它会添加您可以查询的 Win32_AddRemoveProgram WMI Class,它是 Win32_Product 的超快速版本
  • 如果没有,我们可以随时从注册表中检索信息。

这是一个简短的 sn-p,用于在计算机上安装诸如 VLC 之类的应用程序(我没有像你这样的 VNC,所以我正在努力)

Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-object DisplayName -like "VLC*" |Select-Object DisplayName, DisplayVersion, Publisher, InstallDate,UninstallString


DisplayName     : VLC media player
DisplayVersion  : 3.0.8
Publisher       : VideoLAN
InstallDate     :
UninstallString : "C:\Program Files (x86)\VideoLAN\VLC\uninstall.exe"

这个操作要快得多,只有 400 MS 左右。遗憾的是,我们无法更快地使用注册表,因为它有一个非常奇怪的 PowerShell 提供程序,它没有实现 -Filter 参数,所以我们必须检索所有程序,然后过滤到我们想要的选择。

更新您的脚本以改用此函数

我冒昧地重写了您的脚本以使用这种方法,并对其进行了一些重组以提高可读性。

$results = New-object System.Collections.ArrayList
$computers = Get-Content -Path c:\Computers.txt 
foreach ($computer in $computers){
    #get VNC entries from remote computers registry
    $VNCKeys = Invoke-Command -ComputerName $computer -ScriptBlock {
        Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | 
            Where-object DisplayName -like "VNC V*" | 
                Select-Object DisplayName, DisplayVersion, Publisher, UninstallString, @{Name=‘ComputerName‘;Expression={$computer}}
        }#end of remote command

    if ($VNCKeys -ne $null){
        forEach($VNCKey in $VNCKeys){
            [void]$results.Add($VNCKey)
        } 
    }
}

$results | Sort-Object ComputerName | Export-CSV -path c:\VNCInstalled.csv -NoTypeInformation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 2011-05-15
    • 2013-07-28
    • 2020-05-11
    • 1970-01-01
    • 2018-06-28
    相关资源
    最近更新 更多