过滤文件内容以排除 PowerShell 中另一个文件中具有匹配字符串的所有行

Filter a file content to exclude all the lines with matching strings from another file in PowerShell

我需要过滤变量的内容,以从 SubnetExceptions.txt 文件中排除所有具有匹配字符串的行作为过滤器,就像 grep -v 命令一样。

$Value变量经过大量处理以达到我需要的格式。

代码的工作部分如下:

$Value=netsh dhcp server show mibinfo | findstr "Subnet Addresses"
$Value=$Value -replace "Subnet","% `n Subnet"
$Value=$Value -replace "No. of Addresses in use","AddressesInUse"
$Value=$Value -replace "No. of free Addresses","AddressesFree"
$Value=$Value -replace '^.|.$', ' '
$Value=$Value -replace '    ', ""
$Value=$Value -replace '  ', " "
$Value= -join $Value 
$Value=$Value -replace '\n', ''
$Value=$Value -replace "% ", "`n"
$Value=$Value -replace ' = ', '='

ps1 的前 11 行处理该命令的输出 ($Value):

    子网 = 10.1.8.0。
        使用中的地址数 = 11。
        免费地址数 = 18。
    子网 = 10.1.9.0。
        使用中的地址数 = 1。
        免费地址数 = 201。
    子网 = 10.1.11.0。
        使用中的地址数 = 188。
        免费地址数 = 61。
    子网 = 10.1.12.0。
        使用中的地址数 = 207。
        免费地址数 = 44。
    子网 = 10.1.13.0。
        使用中的地址数 = 149。
        免费地址数 = 100。

对此:

子网=10.1.8.0 AddressesInUse=11 AddressesFree=18
子网=10.1.9.0 AddressesInUse=1 AddressesFree=201
子网=10.1.11.0 AddressesInUse=188 AddressesFree=61
子网=10.1.12.0 AddressesInUse=207 AddressesFree=44
子网=10.1.13.0 AddressesInUse=149 AddressesFree=100

这些替换行适用于换行符、点、空格和 "variable=value" 格式。我需要子网值位于同一行,以便我可以将它们过滤掉,从而进行字符串处理。

该命令的实际输出为 278 行,其中有更多相同的行,因此我将其修剪为 5 行以使其保持最小以便在实验室中重现。

过滤文件(C:\Scripts\SubnetExceptions.txt)内容如下:

10.1.12.0
10.1.13.0

这是我要过滤掉的最后两个子网值。这就是到目前为止已经测试过的(将其添加到上述处理的正下方):

$Filter = (Get-Content '.\SubnetExceptions.txt' |
          ForEach-Object {[regex]::Escape($_)}) -join '|'
($Value) -notmatch $Filter | Set-Content '.\output.txt'

预期结果应该是:

子网=10.1.8.0 AddressesInUse=11 AddressesFree=18
子网=10.1.9.0 AddressesInUse=1 AddressesFree=201
子网=10.1.11.0 AddressesInUse=188 AddressesFree=61

由于过滤器,最后两行将被删除,但输出文件只写入值 "false"。

我建议您使用比较对象 cmdlet。

$refference  = get-content C:\text_file1.txt
$difference = get-content C:\text_file2.txt
Compare-Object -ReferenceObject $refference -DifferenceObject $difference -IncludeEqual | Where sideindicator -ne "==" | select -ExpandProperty inputobject

根据第二个文件的内容构建正则表达式:

$re = (Get-Content '.\filter.txt' | ForEach-Object {[regex]::Escape($_)}) -join '|'

然后使用该过滤器从您的第一个文件中排除匹配行:

(Get-Content '.\MyList.txt') -notmatch $re | Set-Content '.\output.txt'

如果您需要将输出写入文件和 STDOUT,请使用 Tee-Object 而不是 Set-Content


编辑:

既然我们可以看到更广阔的前景,我们或许可以提供一些更全面的建议。一方面,我建议将 netsh 输出的处理更改为如下内容:

$Value = netsh dhcp server show mibinfo | findstr "Subnet Addresses" | Out-String
$Value = $Value -replace '(?ms)\.\r?\n\s+No\. of ', "`t"
$Value = $Value -replace 'Addresses in use', 'AddressesInUse'
$Value = $Value -replace 'free Addresses', 'AddressesFree'
$Value = $Value -replace ' '
$Value = $Value -split '\r?\n'
$Value = $Value -replace '\.$'

这将为您提供一个制表符分隔行的列表,这些行可能比您现在拥有的单个字符串更容易处理。

请注意,您也可以在单个语句中以菊花链方式连接上面列出的操作:

$Value = (netsh dhcp server show mibinfo | findstr "Subnet Addresses" | Out-String) `
         -replace '(?ms)\.\r?\n\s+No\. of ', "`t" `
         -replace 'Addresses in use', 'AddressesInUse' `
         ...

但是,尽管有多项分配,我还是会坚持使用您选择的形式,因为它提供了更好的可维护性。您只需 commenting/uncommenting 一行即可轻松禁用或启用单个操作。

对于行列表形式的数据,正则表达式过滤器现在将按预期工作:

$Value -notmatch $Filter | Tee-Object -FilePath '.\output.txt'

以前不起作用的原因是您只有一个字符串而不是字符串数组。


综上所述,我强烈建议重新设计您的解决方案。虽然 PowerShell 完全能够解析和处理字符串,但您忽略了许多可以让您的生活更轻松的功能。

一方面,DHCP cmdlets 会以对象形式为您提供数据,无需解析 netsh 的字符串输出。但即使由于某种原因你不能使用它们,你仍然可以将字符串输出转换为对象,例如像这样:

...
$Value = $Value -split '\r?\n'
$Value = $Value -replace '\.$'
$obj = $Value | ForEach-Object {
    $ht = $_ -replace '\t', "`n" | ConvertFrom-StringData
    New-Object -Type PSObject -Property $ht
}

或者像这样:

$Value = netsh dhcp server show mibinfo | findstr "Subnet Addresses" | Out-String
$Value = $Value -replace '(?ms)\.\r?\n\s+No\. of ', "`t"
$Value = $Value -replace ' '
$obj = $Value -split '\r?\n' | Where-Object {
    $_ -match '=(\d+\.\d+\.\d+\.\d+)\t.*?=(\d+)\t.*?=(\d+)'
} | ForEach-Object {
    New-Object -Type PSObject -Property @{
        'Subnet' = $matches[1]
        'Used'   = [int]$matches[2]
        'Free'   = [int]$matches[3]
    }
}

使用像这样的对象列表形式的数据,您可以将过滤简化为这样的东西,因为您现在可以比较各个属性,而不必匹配部分字符串:

$Filter = Get-Content '.\SubnetExceptions.txt'
$obj | Where-Object {
    $Filter -notcontains $_.Subnet
} | Export-Csv '.\output.csv' -NoType

您还可以将数据导出为 CSV 等结构化格式,这样可以在需要进一步处理数据时简化 transfer/import。