Отфильтровать содержимое файла, чтобы исключить все строки с совпадающими строками из другого файла в PowerShell.

Мне нужно отфильтровать содержимое переменной, чтобы исключить все строки с совпадающими строками из файла SubnetExceptions.txt в качестве фильтра, как и команду grep -v.

Переменная _3 претерпела множество обработок, чтобы добиться нужного мне формата.

Рабочая часть кода выглядит следующим образом:

$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 ' = ', '='

Эти первые 11 строк ps1 обрабатывают вывод ($Value) этой команды из этого:

    Subnet = 10.1.8.0.
        No. of Addresses in use = 11.
        No. of free Addresses = 18.
    Subnet = 10.1.9.0.
        No. of Addresses in use = 1.
        No. of free Addresses = 201.
    Subnet = 10.1.11.0.
        No. of Addresses in use = 188.
        No. of free Addresses = 61.
    Subnet = 10.1.12.0.
        No. of Addresses in use = 207.
        No. of free Addresses = 44.
    Subnet = 10.1.13.0.
        No. of Addresses in use = 149.
        No. of free Addresses = 100.

к этому:

Subnet=10.1.8.0  AddressesInUse=11  AddressesFree=18  
Subnet=10.1.9.0  AddressesInUse=1  AddressesFree=201  
Subnet=10.1.11.0  AddressesInUse=188  AddressesFree=61  
Subnet=10.1.12.0  AddressesInUse=207  AddressesFree=44  
Subnet=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'

Ожидаемый результат должен быть:

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

Где последние 2 строки были бы удалены из-за фильтра, но вместо этого в выходной файл пишется только значение "false".


person David Lago    schedule 14.12.2017    source источник


Ответы (2)


Создайте регулярное выражение из содержимого вашего второго файла:

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

Затем используйте этот фильтр, чтобы исключить совпадающие строки из вашего первого файла:

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

Используйте Tee-Object вместо Set-Content, если вам нужен вывод, записываемый как в файл, так и в STDOUT.


Изменить:

Теперь, когда мы можем видеть более широкую картину, мы могли бы предложить несколько более подробных советов. Во-первых, я бы предложил изменить обработку вывода 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 командлеты, которые предоставят вам данные в форме объекта, устраняя необходимость анализировать строковый вывод 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, что позволит упростить передачу/импорт, если потребуется дальнейшая обработка данных.

person Ansgar Wiechers    schedule 14.12.2017
comment
Похоже, это сработает, но что, если содержимое первого файла уже находится в переменной? Использовать get-variable или выбрать Get-Content $Variable? - person David Lago; 14.12.2017
comment
Просто замените Get-Content 'filename' на $variable. - person Ansgar Wiechers; 14.12.2017
comment
Давайте продолжим обсуждение в чате. - person David Lago; 14.12.2017
comment
Спасибо. Это очень помогло мне выполнить эту работу. Спасибо. - person David Lago; 19.12.2017

Я бы порекомендовал вам использовать командлет compare-object.

$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
person Tesla Great    schedule 14.12.2017
comment
Вы действительно пробовали то, что предлагаете? - person Ansgar Wiechers; 14.12.2017
comment
Извините, у меня была опечатка в переменной. Я проверил, и это работает. сравнил два файла и исключил одинаковые строки. - person Tesla Great; 14.12.2017
comment
Пожалуйста, перечитайте вопрос. ОП предоставил образцы для ввода и желаемого результата. Ваш код создает строки из обоих файлов, которые отличаются (для данного образца ввода, который представляет собой полное содержимое обоих файлов). То, что хочет OP, - это строки из первого файла, которые не содержат строк из второго файла. Кроме того, -IncludeEqual и Where sideindicator -ne "==" бессмысленны, потому что они компенсируют друг друга. - person Ansgar Wiechers; 14.12.2017