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

Dav*_*ago 0 powershell scripting replace windows-scripting

我需要过滤变量的内容,以将所有具有匹配字符串的行从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 ' = ', '='
Run Code Online (Sandbox Code Playgroud)

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

这些替换行适用于换行符,点,空格和“变量=值”格式。我需要将子网值放在同一行中,以便可以将其过滤掉,从而进行字符串处理。

该命令的实际输出为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'
Run Code Online (Sandbox Code Playgroud)

预期结果应为:

子网= 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”。

Ans*_*ers 5

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

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

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

(Get-Content '.\MyList.txt') -notmatch $re | Set-Content '.\output.txt'
Run Code Online (Sandbox Code Playgroud)

如果您需要将输出同时写入文件和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 '\.$'
Run Code Online (Sandbox Code Playgroud)

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

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

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

但是,尽管分配了多项任务,我仍会坚持使用您选择的表单,因为它提供了更好的可维护性。您只需注释/取消注释行即可轻松禁用或启用单个操作。

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

$Value -notmatch $Filter | Tee-Object -FilePath '.\output.txt'
Run Code Online (Sandbox Code Playgroud)

之所以以前不起作用,是因为您只有一个字符串而不是一个字符串数组。


综上所述,我强烈建议您重新设计解决方案。尽管PowerShell完全能够解析和处理字符串,但是您忽略了许多功能,这些功能可能会使您的生活变得更加轻松。

一方面,有DHCP cmdlet将以对象形式为您提供数据,而无需解析的字符串输出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
}
Run Code Online (Sandbox Code Playgroud)

或像这样:

$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]
    }
}
Run Code Online (Sandbox Code Playgroud)

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

$Filter = Get-Content '.\SubnetExceptions.txt'
$obj | Where-Object {
    $Filter -notcontains $_.Subnet
} | Export-Csv '.\output.csv' -NoType
Run Code Online (Sandbox Code Playgroud)

您还可以采用结构化格式(如CSV)导出数据,如果需要进一步处理数据,则可以简化传输/导入。