web-dev-qa-db-fra.com

Regex multiligne pour correspondre au bloc de configuration

J'ai des problèmes pour essayer de faire correspondre un certain bloc de configuration (plusieurs) à partir d'un fichier. Ci-dessous se trouve le bloc que j'essaye d'extraire du fichier de configuration:

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

Il y en a plusieurs comme ça, chacun avec une adresse MAC différente. Comment faire correspondre un bloc de configuration sur plusieurs lignes?

23
Scott

Le premier problème que vous pouvez rencontrer est que pour faire correspondre plusieurs lignes, vous devez traiter le contenu du fichier comme une chaîne unique plutôt que par ligne individuelle. Par exemple, si vous utilisez Get-Content pour lire le contenu du fichier, par défaut, il vous donnera un tableau de chaînes - un élément pour chaque ligne. Pour faire correspondre les lignes, vous voulez que le fichier soit dans une seule chaîne (et j'espère que le fichier n'est pas trop volumineux). Vous pouvez le faire comme ceci:

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

Ou dans PowerShell 3.0, vous pouvez utiliser Get-Content avec le -Raw paramètre:

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

Ensuite, vous devez spécifier une option d'expression régulière pour faire correspondre les terminaisons de ligne, c'est-à-dire.

  • Mode SingleLine (. correspond à n'importe quel caractère y compris saut de ligne), ainsi que
  • Mode multiligne (^ et $ correspond aux terminateurs de ligne intégrés), par exemple.
  • (?smi) - notez que le "i" est d'ignorer la casse

par exemple.:

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
!

Utilisez le Select-String applet de commande pour effectuer la recherche, car vous pouvez spécifier -AllMatches et il affichera toutes les correspondances tandis que le -match L'opérateur s'arrête après la première correspondance. Cela a du sens parce que c'est un opérateur booléen qui a juste besoin de déterminer s'il y a a correspondance.

42
Keith Hill

Dans le cas où cela peut encore être utile à quelqu'un et en fonction des besoins réels, l'expression régulière de la réponse de Keith n'a pas besoin d'être aussi compliquée. Si l'utilisateur veut simplement sortir chaque bloc, les éléments suivants suffiront:

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

Le regex ap71xx[^!]*! fonctionnera mieux et l'utilisation de .* dans une expression régulière n'est pas recommandé car il peut générer des résultats inattendus. Le motif [^!]+! correspondra à n'importe quel caractère à l'exception du point d'exclamation, suivi du point d'exclamation.

Si le début du bloc n'est pas requis dans la sortie, le script mis à jour est:

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

Groups[0] contient toute la chaîne correspondante, Groups[1] contiendra la correspondance de chaîne entre parenthèses dans l'expression régulière.

Si $fileContent n'est requis pour aucun autre traitement, la variable peut être supprimée:

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

Cette expression régulière recherchera le texte ap suivi d'un nombre quelconque de caractères et de nouvelles lignes se terminant par un !:

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

Je m'ennuyais donc un peu. J'ai écrit un script qui décomposera le fichier texte comme vous l'avez décrit (tant qu'il ne contient que les lignes que vous avez affichées). Cela peut fonctionner avec d'autres lignes aléatoires, tant qu'elles ne contiennent pas les mots clés: ap, profil, domaine, nom d'hôte ou zone. Il les importera et vérifiera ligne par ligne pour chacune des propriétés (MAC, profil, domaine, nom d'hôte, zone) et les placera dans un objet qui pourra être utilisé ultérieurement. Je sais que ce n'est pas ce que vous avez demandé, mais comme j'ai passé du temps à y travailler, j'espère que cela pourra être utilisé à bon escient. Voici le script si quelqu'un est intéressé. Il devra être adapté à vos besoins spécifiques:

$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

Voici mon point de vue. Si vous n'avez pas besoin de l'expression rationnelle, vous pouvez utiliser -like ou .contains (). La question ne dit jamais quel est le modèle de recherche. Voici un exemple avec un fichier texte 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