【问题标题】:How to group by all properties in PowerShell?如何按 PowerShell 中的所有属性进行分组?
【发布时间】:2021-10-27 09:26:13
【问题描述】:

我计划在 Windows 上删除重复的防火墙规则。我知道有一组像get-netfirewallrule 这样的防火墙cmdlet,但是当我尝试通过防火墙规则来获取像Get-NetFirewallRule -displayname xxx | Get-NetFirewallApplicationFilter 这样的附加信息时,它太慢了。所以我打算使用老派的方式,netsh advfirewall firewall 命令集。

现在我已将netsh advfirewall firewall 的输出解析为对象数组。

$output = netsh advfirewall firewall show rule name=all verbose | Out-String
$output = [regex]::split($output.trim(), "\r?\n\s*\r?\n");

$objects = @(foreach($section in $output) {
    $obj = [PSCustomObject]@{}

    foreach($line in $($section -split '\r?\n')) {
        if($line -match '^\-+$') {
            continue
        }
        $name, $value = $line -split ':\s*', 2
        $name = $name -replace " ", ""
        
        $obj | Add-Member -MemberType NoteProperty -Name $name -Value $value
    }

    $obj
})

但问题是数组中的对象具有不同的属性。例如,这是一个对象:

RuleName       : HNS Container Networking - ICS DNS (TCP-In) - B15BF139-F18D-471C-A18C-92DFD33350F1 - 0
Description    : HNS Container Networking - ICS DNS (TCP-In) - B15BF139-F18D-471C-A18C-92DFD33350F1 - 0
Enabled        : Yes
Direction      : In
Profiles       : Domain,Private,Public
Grouping       :
LocalIP        : Any
RemoteIP       : Any
Protocol       : TCP
LocalPort      : 53
RemotePort     : Any
Edgetraversal  : No
Program        : C:\WINDOWS\system32\svchost.exe
Service        : sharedaccess
InterfaceTypes : Any
Security       : NotRequired
Rulesource     : Local Setting
Action         : Allow

这是另一个:

RuleName       : HNS Container Networking - DNS (UDP-In) - 91EC1DEF-8CB8-4C2A-A6D4-91480448AE97 - 0
Description    : HNS Container Networking - DNS (UDP-In) - 91EC1DEF-8CB8-4C2A-A6D4-91480448AE97 - 0
Enabled        : Yes
Direction      : In
Profiles       : Domain,Private,Public
Grouping       :
LocalIP        : Any
RemoteIP       : Any
Protocol       : UDP
LocalPort      : 53
RemotePort     : Any
Edgetraversal  : No
InterfaceTypes : Any
Security       : NotRequired
Rulesource     : Local Setting
Action         : Allow

如您所见,第一个规则使用端口以及定义规则的程序,第二个仅使用端口。如何对对象进行分组并查找重复项(以便我可以删除相应的防火墙规则)?我不能简单地使用$objects | Group-Object -Property rulename,enabled,.....,因为并非所有对象的属性都相同。

顺便说一句,重复的防火墙规则是由我过去编写的一些不完善的脚本创建的。

【问题讨论】:

    标签: arrays powershell duplicates grouping firewall


    【解决方案1】:

    在 PowerShell 出现之前,您已经学习(或记得)解析命令输出的艰难方法 ? 但是您已经完成了大部分工作,我们只需要对您的尝试进行一些修改即可获得所有的通用属性返回的对象:

    $output = ( netsh advfirewall firewall show rule name=all verbose |
      Out-String ).Trim() -split '\r?\n\s*\r?\n'
    $propertyNames = [System.Collections.Generic.List[string]]::new()
    
    $objects = @( $(foreach($section in $output ) {
        $obj = @{}
    
        foreach( $line in ($section -split '\r?\n') ) {
            if( $line -match '^\-+$' ) {
                continue
            }
            $name, $value = $line -split ':\s*', 2
            $name = $name -replace " ", ""
            
            $obj.$name  = $value
            if( $propertyNames -notcontains $name ) {
                $propertyNames.Add( $name )
            }
        }
        
        $obj
    }) | ForEach-Object {
        foreach( $prop in $propertyNames ) {
            if( $_.Keys -notcontains $prop ) {
                $_.$prop = $null
            }
        }
        [PSCustomObject]$_
    })
    

    我将解释我对您的代码所做的调整:

    • $output 设置在一行中(此处为多行以便于阅读)。并非绝对必要。
    • 使用-split 运算符而不是[regex]::split() 以与您的其余代码保持一致。
    • 创建了一个名为$propertyNames 的列表。这将用于跟踪从您的输出中读取的所有属性名称。
    • Sub-express $() 您的第一个 foreach 循环以防止需要中间变量,让我们将 foreach 输出传递到管道中。这在最后是必要的。它必须是一个子表达式,而不是仅仅被()包围。
    • $obj 设为hashtable 而不是PSCustomObject。修改对象时使用起来更容易,最后可以转换为PSCustomObject
    • Add-Member 不再需要,因为我们有一个 hashtable,所以 $obj.$name = $value 足以设置属性名称和值。
    • 如果$name 不在$propertyList 中跟踪的已知属性名称中,请添加它。 foreach 部分循环完成后,我们将拥有您的 netsh 命令返回的所有可能属性。
    • 现在我们可以为netsh 输出的每个部分返回$obj。我们可能仍需要更改此设置,因此暂时不要将其转换为 PSCustomObject
    • 将子表达式foreach 循环的输出通过管道传输到ForEach-Object。同样,这防止了对中间变量的需要。我们需要遍历每个$obj,因为......
    • ForEach-Object 内,遍历$propertyList 并检查每个已知属性是否作为每个$obj 上的键存在(在$_$PSitem 内表示为ScriptBlock。如果没有存在,将其添加为$null
    • 最后,在我们返回时将 $_(将是从第一个循环返回的每个 $obj)转换为 PSCustomItem

    这是您的代码与我的更改的差异:

    现在,每个返回的对象都应该具有相同的属性,即使它们没有显示在该规则的原始输出中。

    【讨论】:

    • 非常感谢您的详细解答!是的,我已经好几年没有使用 PowerShell 了。我从你的回答中学到了很多。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 2018-11-16
    • 2021-11-25
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 2016-02-21
    相关资源
    最近更新 更多