PNPUtil 检索每个驱动程序并使用 PSObject 添加到数组

PNPUtil retrieve each driver and add to array with PSObject

我使用 Powershell 检索所有安装的第三方驱动程序:

$PNPDrivers = PNPUtil /Enum-Drivers

检索到的信息格式如下:

Microsoft PnP Utility

Published Name:     oem19.inf
Original Name:      apollolakesystem.inf
Provider Name:      INTEL
Class Name:         Systemenheter
Class GUID:         {4d36e97d-e325-11ce-bfc1-08002be10318}
Driver Version:     07/18/1968 10.1.17.1
Signer Name:        Microsoft Windows Hardware Compatibility Publisher

Published Name:     oem20.inf
Original Name:      avotonsystem.inf
Provider Name:      INTEL
Class Name:         Systemenheter
Class GUID:         {4d36e97d-e325-11ce-bfc1-08002be10318}
Driver Version:     07/18/1968 10.1.3.1
Signer Name:        Microsoft Windows Hardware Compatibility Publisher

我正在尝试制作一个 foreach 循环,以便将每个驱动程序添加到一个带有 PSObject 的数组中,以便我稍后可以在脚本中获取所需的信息。

我尝试了很多组合,但都没有成功。 现在我已经尝试了一个正则表达式并将每一行分组以指定每个值但是我没有得到任何数据。

下面是我使用的 Regex 表达式,它似乎有效?

在那之后,我尝试按照 link 中的信息进行操作:Parsing Text with PowerShell 只需更改信息以匹配我的代码:

$Pattern = "
^(?<PublishedName>Published Name:.*)
^(?<OriginalName>Original Name:.*)
^(?<ProviderName>Provider Name:.*)
^(?<ClassName>Class Name:.*)
^(?<ClassGUID>Class GUID:.*)
^(?<DriverVersion>Driver Version:.*)
^(?<SignerName>Signer Name:.*)
"

$PNPDrivers |
    Select-String -Pattern $Pattern |
    Foreach-Object {
        # here we access the groups by name instead of by index
        $PublishedName, $OriginalName, $ProviderName, $ClassName, $ClassGUID, $DriverVersion, $SignerName = $_.Matches[0].Groups['PublishedName', 'OriginalName', 'ProviderName', 'ClassName', 'ClassGUID', 'DriverVersion', 'SignerName'].Value
        [PSCustomObject] @{
            PublishedName = $PublishedName
            OriginalName = $OriginalName
            ProviderName = $ProviderName
            ClassName = $ClassName
            ClassGUID = $ClassGUID
            DriverVersion = $DriverVersion
            SignerName = $SignerName
        }
    }

但是没有打印出任何数据,所以我一定是遗漏了一些,但我没能找到什么。 我怎样才能检索数据并为每个驱动程序使用它?

第二次尝试

下面的代码给了我所有的值(我认为)但它把它分成一行。 我想这是因为它没有同时匹配所有 rexeg:s。 这可以更改吗?

$Pattern = "(^(?<PublishedName>^Published Name:\s*([^\n\r]*)))|(^(?<OriginalName>^Original Name:\s*([^\n\r]*)))|(^(?<ProviderName>^Provider Name:\s*([^\n\r]*)))|(^(?<ClassName>^Class Name:\s*([^\n\r]*)))|(^(?<ClassGUID>^Class GUID:\s*([^\n\r]*)))|(^(?<DriverVersion>^Driver Version:\s*([^\n\r]*)))|(^(?<SignerName>^Signer Name:\s*([^\n\r]*)))"

$PNPDrivers |
    Select-String -Pattern $Pattern -AllMatch |
    Foreach-Object {
        # here we access the groups by name instead of by index
        $PublishedName, $OriginalName, $ProviderName, $ClassName, $ClassGUID, $DriverVersion, $SignerName = $_.Matches[0].Groups['PublishedName', 'OriginalName', 'ProviderName', 'ClassName', 'ClassGUID', 'DriverVersion', 'SignerName'].Value
        [PSCustomObject] @{
            PublishedName = $PublishedName -replace "Published Name:     ",""
            OriginalName = $OriginalName -replace "Original Name:      ",""
            ProviderName = $ProviderName -replace "Provider Name:      ",""
            ClassName = $ClassName -replace "Class Name:         ",""
            ClassGUID = $ClassGUID -replace "Class GUID:         ",""
            DriverVersion = $DriverVersion -replace "Driver Version:     ",""
            SignerName = $SignerName -replace "Signer Name:        ",""
        }
    }

第三次尝试

快到了!很近! 你就是那个男人 !

因为我要在我们的 SCCM 环境中遍历驱动程序包,所以使用 [PSCustomObject] 似乎是正确的方法。

尽管仍然存在的问题似乎是某些行“不同步”并显示错误的列值。

关于如何纠正这个问题有什么想法吗?

第四次尝试

工作代码!!

$List = New-Object System.Collections.ArrayList
((PNPUtil /Enum-Drivers | 
Select-Object -Skip 2) | 
Select-String -Pattern 'Published Name:' -Context 0,7) | 
ForEach {
if($PSItem.Context.PostContext[4] -like "*Class Version:*"){
$ClassVersion = $PSItem.Context.PostContext[4] -replace '.*:\s+'
$DriverVersion = $PSItem.Context.PostContext[5] -replace '.*:\s+'
$SignerName = $PSItem.Context.PostContext[6] -replace '.*:\s+'
}else{
$ClassVersion = "N/A"
$DriverVersion = $PSItem.Context.PostContext[4] -replace '.*:\s+'
$SignerName = $PSItem.Context.PostContext[5] -replace '.*:\s+'
}
    $y = New-Object PSCustomObject
        $y | Add-Member -Membertype NoteProperty -Name PublishedName -value (($PSitem | Select-String -Pattern 'Published Name:' ) -replace '.*:\s+')
        $y | Add-Member -Membertype NoteProperty -Name OriginalName -value (($PSItem.Context.PostContext[0]) -replace '.*:\s+')
        $y | Add-Member -Membertype NoteProperty -Name ProviderName -value (($PSItem.Context.PostContext[1]) -replace '.*:\s+')
        $y | Add-Member -Membertype NoteProperty -Name ClassName -value (($PSItem.Context.PostContext[2]) -replace '.*:\s+')
        $y | Add-Member -Membertype NoteProperty -Name ClassGUID -value (($PSItem.Context.PostContext[3]) -replace '.*:\s+')
        $y | Add-Member -Membertype NoteProperty -Name ClassVersion -value $ClassVersion
        $y | Add-Member -Membertype NoteProperty -Name DriverVersion -value $DriverVersion
        $y | Add-Member -Membertype NoteProperty -Name SignerName -value $SignerName
        $z = $List.Add($y)
}

假设你只是 monitors 那么你就可以做到这一点,根本不需要使用 PSCustomObject。

Clear-Host
(PNPUtil /Enum-Drivers /class Display | 
Select-Object -Skip 2) | 
Select-String -Pattern 'Monitors' -Context 3,4
# Results
<#
    Published Name:     oem...
    Original Name:      tp...
    Provider Name:      L...
> Class Name:         Monitors
    Class GUID:         {4d...
    Driver Version:     11...
    Signer Name:        Micr...

    Published Name:     oe...
    Original Name:      tp...
    Provider Name:      L...
> Class Name:         Monitors
    Class GUID:         {4d36...
    Driver Version:     06...
    Signer Name:        Micros...
#>

# The cherry-pick as needed by index, string, string value, etc..
Clear-Host
((PNPUtil /Enum-Drivers /class Display | 
Select-Object -Skip 2) | 
Select-String -Pattern 'Monitors' -Context 3,4)[0]
# Results
<#
  Published Name:     oe...
  Original Name:      tpl...
  Provider Name:      L...
> Class Name:         Monitors
  Class GUID:         {4d36e...
  Driver Version:     11/...
  Signer Name:        Microso...
#>

Clear-Host 
(PNPUtil /Enum-Drivers /class Display | 
Select-Object -Skip 2 | 
Select-String -Pattern 'Monitors' -Context 3,4 | 
ForEach-Object {$PSItem.Context.PreContext[0]})
# Results
<#
Published Name:     oem...
Published Name:     oem...
#>

Clear-Host 
(PNPUtil /Enum-Drivers /class Display | 
Select-Object -Skip 2 | 
Select-String -Pattern 'Monitors' -Context 3,4 | 
ForEach-Object {$PSItem.Context.PreContext[0] -replace '.*:\s+'})
# Results
<#
oem...
oem...
#>

以上回复来自与我和其他人已经回复过的类似的回复。

Parse pnputil output to published name for specific class

你们是在同一个course/class,还是在同一个公司做同样的事情,看谁能先到? ;-}

如果您一次只想获取一个 driver 的信息,只需更改过滤器字符串和 cherry-pick 您之后的行。

然而,如果您真的非常想使用 [PSCustomObject],那么您仍然可以使用上面的内容并重构它。

Clear-Host
((PNPUtil /Enum-Drivers /class Display | 
Select-Object -Skip 2) | 
Select-String -Pattern 'Class Name:' -Context 3,4) | 
ForEach {
    [PSCustomObject]@{
        PublishedName = $PSItem.Context.PreContext[0] -replace '.*:\s+'
        OriginalName  = $PSItem.Context.PreContext[1] -replace '.*:\s+'
        ProviderName  = $PSItem.Context.PreContext[2] -replace '.*:\s+'
        ClassName     = ($PSitem | Select-String -Pattern 'Class Name:') -replace '.*:\s+'
        ClassGUID     = $PSItem.Context.PostContext[0] -replace '.*:\s+'
        DriverVersion = $PSItem.Context.PostContext[1] -replace '.*:\s+'
        SignerName    = $PSItem.Context.PostContext[2] -replace '.*:\s+'   
    }
}
# Results
<#
PublishedName : oe...
OriginalName  : am...
ProviderName  : A...
ClassName     : Syst...
ClassGUID     : {4d36...
DriverVersion : 02/...
SignerName    : Microso...
...
PublishedName : oem...
OriginalName  : w...
ProviderName  : W...
ClassName     : W...
ClassGUID     : {849...
DriverVersion : 11/...
SignerName    : Microsof...
#>

或table格式:

Clear-Host
((PNPUtil /Enum-Drivers /class Display | 
Select-Object -Skip 2) | 
Select-String -Pattern 'Class Name:' -Context 3,4) | 
ForEach {
    [PSCustomObject]@{
      PublishedName = $PSItem.Context.PreContext[0] -replace '.*:\s+'
      OriginalName  = $PSItem.Context.PreContext[1] -replace '.*:\s+'
      ProviderName  = $PSItem.Context.PreContext[2] -replace '.*:\s+'
      ClassName     = ($PSitem | Select-String -Pattern 'Class Name:') -replace '.*:\s+'
      ClassGUID     = $PSItem.Context.PostContext[0] -replace '.*:\s+'
      DriverVersion = $PSItem.Context.PostContext[1] -replace '.*:\s+'
      SignerName    = $PSItem.Context.PostContext[2] -replace '.*:\s+'   
    }
} | 
Format-Table -AutoSize
# Results
<#
PublishedName OriginalName   ProviderName                   ClassName                         ClassGUID                              DriverVersion         SignerName                        
------------- ------------   ------------                   ---------                         ---------                              -------------         ----------                        
...
#>

根据您的评论更新...

sometimes, some drivers apparently also displays a Class Version just below Class GUID. When it does that row gets out of sync. Is it possible to, I don't know, using a IF-statement that if PostContext[3] exists change the order for the PostContext?

...以及我对此的回应。

我对这个用例进行了更多尝试,可以重构我的代码以解决每个 driver.

可能发生的这些潜在 custom/dynamic 字段

在我的系统上,我有两个客户 driver 属性:

Extension IDClass Version.

Context-wise,他们在同一个地方,所以,我这样称呼他们。因此,代码提取数据并使用自定义字段来保存或 none 的值。因此,这意味着您需要首先发现任何此类项目,然后根据需要更改代码。注意:我使用了一个 Switch 块来处理这个问题,并 IF/then 根据案例中需要的字段来完成。

Clear-Host
# Parse/format PnpUtil output based on JSON details, customize for dynamic fields
$PNPDrivers            = PNPUtil /Enum-Drivers
$StringDataRegEx       = '.*:\s+'
$RemoveClassNamesRegEx = 'Extension ID|Class Version'

$CSvHeaders = @(
    'PublishedName', 
    'OriginalName', 
    'ProviderName', 
    'ClassName', 
    'ClassGUID', 
    'ClassVersion_or_ExtensionGUID', 
    'DriverVersion', 
    'SignerName' 
)

$ClassVersionElements = @(
    'Extension ID', 
    'Class Version', 
    'Signer Name'
)

$ClassVersionElements |
ForEach {
    $ElementString = $PSItem
    $ContextID     = If ($ElementString -match $RemoveClassNamesRegEx)
                    {5} 
                    Else {6}

    $PNPDrivers | 
    Select-String -Pattern $ElementString -Context $ContextID | 
    ForEach {
                $DriverData = $PSItem
                switch ($ElementString)
                {
                    'Extension ID' 
                    {
                        [PSCustomObject]@{
                            PublishedName = $DriverData.Context.PreContext[0] -replace $StringDataRegEx
                            OriginalName  = $DriverData.Context.PreContext[1] -replace $StringDataRegEx
                            ProviderName  = $DriverData.Context.PreContext[2] -replace $StringDataRegEx
                            ClassName     = $DriverData.Context.PreContext[3] -replace $StringDataRegEx
                            ClassGUID     = $DriverData.Context.PreContext[4] -replace $StringDataRegEx
                            ClassVersion_or_ExtensionID  = (
                                                             $DriverData | 
                                                             Select-String -Pattern $ElementString
                                                           ) -replace $StringDataRegEx 
                            DriverVersion = $DriverData.Context.PostContext[0] -replace $StringDataRegEx
                            SignerName    = $DriverData.Context.PostContext[1] -replace $StringDataRegEx
                        }            
                    }
                    'Class Version' 
                    {
                        [PSCustomObject]@{
                            PublishedName = $DriverData.Context.PreContext[0] -replace $StringDataRegEx
                            OriginalName  = $DriverData.Context.PreContext[1] -replace $StringDataRegEx
                            ProviderName  = $DriverData.Context.PreContext[2] -replace $StringDataRegEx
                            ClassName     = $DriverData.Context.PreContext[3] -replace $StringDataRegEx 
                            ClassGUID     = $DriverData.Context.PreContext[4] -replace $StringDataRegEx
                            ClassVersion_or_ExtensionID  = (
                                                             $DriverData | 
                                                             Select-String -Pattern $ElementString
                                                           ) -replace $StringDataRegEx 
                            DriverVersion = $DriverData.Context.PostContext[0] -replace $StringDataRegEx
                            SignerName    = $DriverData.Context.PostContext[1] -replace $StringDataRegEx
                        }                      
                    }
                    'Signer Name' 
                    {
                        If ($DriverData -NotMatch $RemoveClassNamesRegEx)
                        {
                            [PSCustomObject]@{
                                PublishedName = $DriverData.Context.PreContext[0] -replace $StringDataRegEx
                                OriginalName  = $DriverData.Context.PreContext[1] -replace $StringDataRegEx
                                ProviderName  = $DriverData.Context.PreContext[2] -replace $StringDataRegEx
                                ClassName     = $DriverData.Context.PreContext[3] -replace $StringDataRegEx
                                ClassGUID     = $DriverData.Context.PreContext[4] -replace $StringDataRegEx
                                ClassVersion_or_ExtensionID  = 'Property not valid for this driver' 
                                DriverVersion = $DriverData.Context.PreContext[5] -replace $StringDataRegEx
                                SignerName    = (
                                                    $DriverData | 
                                                    Select-String -Pattern $ElementString
                                                ) -replace $StringDataRegEx   
                            }
                        }                  
                    }

              }
        }
} | 
Format-Table -AutoSize