web-dev-qa-db-ja.com

構成ブロックに一致する複数行の正規表現

ファイルから特定の構成ブロック(複数の構成ブロック)を照合しようとすると、いくつかの問題が発生します。以下は、構成ファイルから抽出しようとしているブロックです。

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

このようなものが複数あり、それぞれに異なるMACアドレスがあります。複数の行にわたって構成ブロックを一致させるにはどうすればよいですか?

23
Scott

遭遇する可能性のある最初の問題は、複数の行にまたがって一致するために、ファイルの内容を個々の行ではなく単一の文字列として処理する必要があることです。たとえば、Get-Contentを使用してファイルの内容を読み取る場合、既定では、文字列の配列(各行に1つの要素)が提供されます。複数の行にまたがって一致させるには、ファイルを単一の文字列にしたい(そしてファイルが大きすぎないことを願う)。次のようにこれを行うことができます:

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

または、PowerShell 3.0では、-Rawパラメーターを指定してGet-Contentを使用できます。

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

次に、行末記号全体で一致する正規表現オプションを指定する必要があります.

  • SingleLineモード(.は任意の文字と一致しますを含む改行)、および
  • 複数行モード(^および$は埋め込み行ターミネータに一致)、例:.
  • (?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コマンドレットを使用して検索を実行できます。 a一致があるかどうかを判断するだけのブール演算子であるため、意味があります。

42
Keith Hill

これがまだ誰かにとって価値があり、実際の要件によっては、キースの答えの正規表現はそれほど複雑である必要はありません。ユーザーが単に各ブロックを出力したい場合は、次で十分です。

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

正規表現ap71xx[^!]*!のパフォーマンスが向上し、正規表現での.*の使用は、予期しない結果を生成する可能性があるため推奨されません。パターン[^!]+!は、感嘆符とそれに続く感嘆符を除くすべての文字に一致します。

出力でブロックの開始が必要ない場合、更新されたスクリプトは次のとおりです。

$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
David Clarke

この正規表現は、apの後に!で終わる任意の数の文字と改行が続くテキストを検索します。

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

だから私は少し退屈していました。説明したとおりにテキストファイルを分割するスクリプトを作成しました(表示した行のみが含まれている場合)。キーワード、ap、profile、domain、hostname、またはareaが含まれていない限り、他のランダムな行で機能する可能性があります。それらをインポートし、プロパティ(MAC、プロファイル、ドメイン、ホスト名、エリア)ごとに1行ずつチェックし、後で使用できるオブジェクトに配置します。私はこれがあなたが求めたものではないことを知っていますが、私がそれに時間を費やしたので、うまくいけばそれが何らかの利益のために使用できることを願っています。誰かが興味がある場合のスクリプトは次のとおりです。特定のニーズに合わせて調整する必要があります。

$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
2
Nick

これが私の見解です。正規表現が必要ない場合は、-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 
0
js2010