过滤文件内容以排除 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。
我需要过滤变量的内容,以从 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。