【问题标题】:Filter a file content to exclude all the lines with matching strings from another file in PowerShell过滤文件内容以从 PowerShell 中的另一个文件中排除所有具有匹配字符串的行
【发布时间】:2018-05-28 14:56:28
【问题描述】:

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

$Valuevariable 经历了很多处理以达到我需要的格式。

代码的工作部分如下:

$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”。

【问题讨论】:

    标签: powershell replace scripting windows-scripting


    【解决方案1】:

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

    $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' `
             ...
    

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

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

    $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)导出数据,以便在需要进一步处理数据时简化传输/导入。

    【讨论】:

    • 看起来它可以完成这项工作,但是如果第一个文件的内容已经在变量中怎么办?我应该使用“get-variable”还是应该选择“Get-Content $Variable”?
    • 只需将Get-Content 'filename' 替换为$variable
    • 谢谢。它对我完成这项工作有很大帮助。谢谢。
    【解决方案2】:

    我建议您使用 compare-object 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
    

    【讨论】:

    • 你真的尝试你在这里的建议吗?
    • 对不起,我在变量中有错字。我已经测试过了,它可以工作。比较两个文件并排除相同的字符串。
    • 请重新阅读问题。 OP 提供了输入和所需输出的样本。您的代码从两个文件中生成不同的行(对于给定的示例输入,即两个文件的全部内容)。 OP 想要的是第一个文件中不包含第二个文件中的字符串的行。此外,-IncludeEqualWhere sideindicator -ne "==" 毫无意义,因为它们相互抵消了。
    猜你喜欢
    • 2021-08-20
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多