Подтвердить что ты не робот

Многострочное регулярное выражение для соответствия конфигурационному блоку

У меня возникают некоторые проблемы, связанные с определением определенного блока конфигурации (нескольких) из файла. Ниже приведен блок, который я пытаюсь извлечь из файла конфигурации:

ap71xx 00-01-23-45-67-89
 use profile PROFILE
 use rf-domain DOMAIN
 hostname ACCESSPOINT
 area inside
!

Есть несколько таких, как это, каждый с другим MAC-адресом. Как сопоставить конфигурационный блок по нескольким строкам?

4b9b3361

Ответ 1

Первой проблемой, с которой вы можете столкнуться, является то, что для сопоставления нескольких строк вам необходимо обработать содержимое файла как одну строку, а не отдельную строку. Например, если вы используете Get-Content для чтения содержимого файла, то по умолчанию он предоставит вам массив строк - один элемент для каждой строки. Чтобы сопоставить строки, вы хотите, чтобы файл был в одной строке (и надеемся, что файл не слишком большой). Вы можете сделать это так:

$fileContent = [io.file]::ReadAllText("C:\file.txt")

Или в PowerShell 3.0 вы можете использовать Get-Content с параметром -Raw:

$fileContent = Get-Content c:\file.txt -Raw

Затем вам нужно указать опцию регулярного выражения для соответствия между терминаторами строк, т.е.

  • Режим SingleLine (. соответствует любому char, включая строку), а также
  • Многострочный режим (^ и $ соответствует встроенным терминаторам строк), например
  • (?smi) - обратите внимание, что "i" означает игнорировать регистр

например:.

C:\> $fileContent | Select-String '(?smi)([0-9a-f]{2}(-|\s*$)){6}.*?!' -AllMatches |
        Foreach {$_.Matches} | Foreach {$_.Value}

00-01-23-45-67-89
 use profile PROFILE
 use rf-domain DOMAIN
 hostname ACCESSPOINT
 area inside
!
00-01-23-45-67-89
 use profile PROFILE
 use rf-domain DOMAIN
 hostname ACCESSPOINT
 area inside
!

Используйте командлет Select-String, чтобы выполнить поиск, потому что вы можете указать -AllMatches, и он выведет все совпадения, тогда как оператор -match остановится после первого совпадения. Имеет смысл, потому что это булев оператор, который просто должен определить, есть ли совпадение.

Ответ 2

В случае, если это может быть полезным для кого-то и в зависимости от фактического требования, регулярное выражение в ответе Кейта не должно быть таким сложным. Если пользователь просто хочет вывести каждый блок, будет достаточно:

$fileContent = [io.file]::ReadAllText("c:\file.txt")
$fileContent |
    Select-String '(?smi)ap71xx[^!]+!' -AllMatches |
    %{ $_.Matches } |
    %{ $_.Value }

Регулярное выражение ap71xx[^!]*! будет работать лучше, а использование .* в регулярном выражении не рекомендуется, так как оно может генерировать неожиданные результаты. Шаблон [^!]+! будет соответствовать любому символу, кроме восклицательного знака, за которым следует восклицательный знак.

Если начальный блок не требуется в выводе, обновленный script:

$fileContent |
    Select-String '(?smi)ap71xx([^!]+!)' -AllMatches |
    %{ $_.Matches } |
    %{ $_.Groups[1] } |
    %{ $_.Value }

Groups[0] содержит всю согласованную строку, Groups[1] будет содержать совпадение строк в круглых скобках в регулярном выражении.

Если $fileContent не требуется для дальнейшей обработки, переменная может быть устранена:

[io.file]::ReadAllText("c:\file.txt") |
    Select-String '(?smi)ap71xx([^!]+!)' -AllMatches |
    %{ $_.Matches } |
    %{ $_.Groups[1] } |
    %{ $_.Value }

Ответ 3

Это регулярное выражение будет искать текст ap, за которым следует любое количество символов и новые строки, заканчивающиеся на !:

(?si)(a).+?\!{1}

Так что мне было немного скучно. Я написал script, который разбивает текстовый файл, как вы описали (пока он содержит только строки, которые вы отображали). Он может работать с другими случайными строками, если они не содержат ключевых слов: ap, профиль, домен, имя хоста или область. Он будет импортировать их и проверять строки за строкой для каждого из свойств (MAC, Profile, domain, hostname, area) и поместить их в объект, который может быть использован позже. Я знаю, что это не то, о чем вы просили, но так как я потратил время на это, надеюсь, это может быть полезно для хорошего. Вот script, если кто-то заинтересован. Его необходимо будет настроить под ваши конкретные потребности:

$Lines = Get-Content "c:\test\test.txt"
$varObjs = @()
for ($num = 0; $num -lt $lines.Count; $num =$varLast ) {
    #Checks to make sure the line isn't blank or a !. If it is, it skips to next line
    if ($Lines[$num] -match "!") {
        $varLast++
        continue
    }
    if (([regex]::Match($Lines[$num],"^\s.*$")).success) {
        $varLast++
        continue
    }
    $Index = [array]::IndexOf($lines, $lines[$num])
    $b=0
    $varObj = New-Object System.Object
    while ($Lines[$num + $b] -notmatch "!" ) {
        #Checks line by line to see what it matches, adds to the $varObj when it finds what it wants.
        if ($Lines[$num + $b] -match "ap") { $varObj | Add-Member -MemberType NoteProperty -Name Mac -Value $([regex]::Split($lines[$num + $b],"\s"))[1] }
        if ($lines[$num + $b] -match "profile") { $varObj | Add-Member -MemberType NoteProperty -Name Profile -Value $([regex]::Split($lines[$num + $b],"\s"))[3] }
        if ($Lines[$num + $b] -match "domain") { $varObj | Add-Member -MemberType NoteProperty -Name rf-domain -Value $([regex]::Split($lines[$num + $b],"\s"))[3] }
        if ($Lines[$num + $b] -match "hostname") { $varObj | Add-Member -MemberType NoteProperty -Name hostname -Value $([regex]::Split($lines[$num + $b],"\s"))[2] }
        if ($Lines[$num + $b] -match "area") { $varObj | Add-Member -MemberType NoteProperty -Name area -Value $([regex]::Split($lines[$num + $b],"\s"))[2] }
        $b ++
    } #end While
    #Adds the $varObj to $varObjs for future use
    $varObjs += $varObj
    $varLast = ($b + $Index) + 2
}#End for ($num = 0; $num -lt $lines.Count; $num = $varLast)
#displays the $varObjs
$varObjs

Ответ 4

Вот мой дубль. Если вам не нужно регулярное выражение, вы можете использовать -like или .contains(). Вопрос никогда не говорит, что такое шаблон поиска. Вот пример с текстовым файлом Windows.

$file = (get-content -raw file.txt) -replace "'r"  # avoid the line ending issue

$pattern = 'two
three
f.*' -replace "'r"

# just showing what they really are
$file -replace "'r",'\r' -replace "'n",'\n'
$pattern -replace "'r",'\r' -replace "'n",'\n'

$file -match $pattern

$file | select-string $pattern -quiet