web-dev-qa-db-fra.com

Comment imprimer des lignes entre deux modèles, inclusifs ou exclusifs (dans sed, AWK ou Perl)?

J'ai un fichier comme celui-ci et j'aimerais imprimer les lignes entre deux motifs donnés PAT1 et PAT2.

1
2
PAT1
3    - first block
4
PAT2
5
6
PAT1
7    - second block
PAT2
8
9
PAT1
10    - third block

J'ai lu Comment sélectionner des lignes entre deux modèles de marqueur pouvant se produire plusieurs fois avec awk/sed mais je suis curieux de voir toutes les combinaisons possibles de cela, en incluant ou en excluant le modèle.

Comment puis-je imprimer toutes les lignes entre deux modèles?

38
fedorqui

Imprimer des lignes entre PAT1 et PAT2

$ awk '/PAT1/,/PAT2/' file
PAT1
3    - first block
4
PAT2
PAT1
7    - second block
PAT2
PAT1
10    - third block

Ou, en utilisant des variables:

awk '/PAT1/{flag=1} flag; /PAT2/{flag=0}' file

Comment cela marche-t-il?

  • /PAT1/ correspond aux lignes contenant ce texte, ainsi que /PAT2/ Est-ce que.
  • /PAT1/{flag=1} définit le flag lorsque le texte PAT1 se trouve dans une ligne.
  • /PAT2/{flag=0} désactive le flag lorsque le texte PAT2 se trouve dans une ligne.
  • flag est un motif avec l'action par défaut, qui consiste à print $0: si flag est égal à 1, la ligne est imprimée. De cette façon, toutes les lignes apparaissant à partir du moment PAT1 se produit et jusqu’à la prochaine PAT2 est vu. Cela affichera également les lignes de la dernière correspondance de PAT1 jusqu'à la fin du fichier.

Lignes d'impression entre PAT1 et PAT2 - n'incluant pas PAT1 et PAT2

$ awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' file
3    - first block
4
7    - second block
10    - third block

Ceci utilise next pour ignorer la ligne contenant PAT1 afin d'éviter que cela ne soit imprimé.

Cet appel à next peut être abandonné en remaniant les blocs: awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file.

Imprimer des lignes entre PAT1 et PAT2 - y compris PAT1

$ awk '/PAT1/{flag=1} /PAT2/{flag=0} flag' file
PAT1
3    - first block
4
PAT1
7    - second block
PAT1
10    - third block

En plaçant flag à la toute fin, il déclenche l'action définie sur PAT1 ou PAT2: pour imprimer sur PAT1 et non sur PAT2.

Imprimer des lignes entre PAT1 et PAT2 - y compris PAT2

$ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file
3    - first block
4
PAT2
7    - second block
PAT2
10    - third block

En plaçant flag au tout début, il déclenche l'action définie précédemment et affiche donc le motif de fermeture mais pas celui de départ.

Imprimer des lignes entre PAT1 et PAT2 - exclure les lignes du dernier PAT1 jusqu'à la fin du fichier si aucun autre PAT2 ne se produit

Ceci est basé sur ne solution d'Ed Morton .

awk 'flag{
        if (/PAT2/)
           {printf "%s", buf; flag=0; buf=""}
        else
            buf = buf $0 ORS
     }
     /PAT1/ {flag=1}' file

En une ligne:

$ awk 'flag{ if (/PAT2/){printf "%s", buf; flag=0; buf=""} else buf = buf $0 ORS}; /PAT1/{flag=1}' file
3    - first block
4
7    - second block

# note the lack of third block, since no other PAT2 happens after it

Cela conserve toutes les lignes sélectionnées dans un tampon qui est rempli à partir du moment où PAT1 est trouvé. Ensuite, il continue à être rempli avec les lignes suivantes jusqu'à ce que PAT2 soit trouvé. À ce stade, le contenu stocké est imprimé et le tampon est vidé.

63
fedorqui

Utiliser grep avec PCRE (le cas échéant) pour imprimer des marqueurs et des lignes entre les marqueurs:

$ grep -Pzo "(?s)(PAT1(.*?)(PAT2|\Z))" file
PAT1
3    - first block
4
PAT2
PAT1
7    - second block
PAT2
PAT1
10    - third block
  • -P Perl-regexp, PCRE. Pas dans toutes les grep variantes
  • -z Traiter l’entrée comme un ensemble de lignes, chacune terminée par un octet nul au lieu d’une nouvelle ligne
  • -o imprimer uniquement les correspondances
  • (?s) DotAll, c'est-à-dire. dot trouve aussi les nouvelles lignes
  • (.*?) trouvaille non fictive
  • \Z Match uniquement à la fin de la chaîne ou avant la nouvelle ligne à la fin

Imprimer les lignes entre les marqueurs à l'exclusion du marqueur de fin:

$ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file
PAT1
3    - first block
4
PAT1
7    - second block
PAT1
10    - third block
  • (.*?)(?=(\nPAT2|\Z)) trouvaille non truquée avec lookahead pour \nPAT2 et \Z

Imprimer les lignes entre les marqueurs à l'exclusion des marqueurs:

$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file
3    - first block
4
7    - second block
10    - third block
  • (?<=PAT1\n) lookbehind positif pour PAT1\n

Imprimer les lignes entre les marqueurs, à l’exception du marqueur de début:

$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(PAT2|\Z))" file
3    - first block
4
PAT2
7    - second block
PAT2
10    - third block
5
James Brown

Voici une autre approche

Inclure les deux modèles (par défaut)

$ awk '/PAT1/,/PAT2/' file
PAT1
3    - first block
4
PAT2
PAT1
7    - second block
PAT2
PAT1
10    - third block

masque les deux modèles

$ awk '/PAT1/,/PAT2/{if(/PAT2|PAT1/) next; print}' file
3    - first block
4
7    - second block
10    - third block

masque de départ masque

$ awk '/PAT1/,/PAT2/{if(/PAT1/) next; print}' file
3    - first block
4
PAT2
7    - second block
PAT2
10    - third block

modèle de fin de masque

$ awk '/PAT1/,/PAT2/{if(/PAT2/) next; print}' file
PAT1
3    - first block
4
PAT1
7    - second block
PAT1
10    - third block
4
karakfa

Pour être complet, voici une solution Perl:

Imprimer des lignes entre PAT1 et PAT2 - inclure PAT1 et PAT2

Perl -ne '/PAT1/../PAT2/ and print' FILE

ou:

Perl -ne 'print if /PAT1/../PAT2/' FILE

Imprimer des lignes entre PAT1 et PAT2 - exclure PAT1 et PAT2

Perl -ne '/PAT1/../PAT2/ and !/PAT1/ and !/PAT2/ and print' FILE

ou:

Perl -ne 'if (/PAT1/../PAT2/) {print unless /PAT1/ or /PAT2/}' FILE 

Imprimer des lignes entre PAT1 et PAT2 - exclure PAT1 uniquement

Perl -ne '/PAT1/../PAT2/ and !/PAT1/ and print' FILE

Imprimer des lignes entre PAT1 et PAT2 - exclure PAT2 uniquement

Perl -ne '/PAT1/../PAT2/ and !/PAT2/ and print' FILE

Voir également:

  • Section opérateur de plage dans perldoc perlop Pour plus d'informations sur la grammaire /PAT1/../PAT2/:

Opérateur de plage

... Dans un contexte scalaire, ".." renvoie une valeur booléenne. L'opérateur est bistable, à la manière d'une bascule, et émule l'opérateur de la plage (virgule) de sed, awk et de divers éditeurs.

  • Pour l'option -n, Voir perldoc perlrun, Ce qui fait que Perl se comporte comme sed -n.

  • Perl Cookbook, 6.8 pour une discussion détaillée sur l'extraction d'une plage de lignes.

1
Alex Harvey

Alternativement:

sed '/START/,/END/!d;//d'

Ceci supprime toutes les lignes sauf celles entre et incluant START et END, puis le //d supprime les lignes START et END depuis // _ oblige sed à utiliser les modèles précédents.

1
Daedelus

Vous pouvez faire ce que vous voulez avec sed en en supprimant l’impression normale de l’espace motif avec -n. Par exemple pour include les motifs dans le résultat que vous pouvez faire:

$ sed -n '/PAT1/,/PAT2/p' filename
PAT1
3    - first block
4
PAT2
PAT1
7    - second block
PAT2
PAT1
10    - third block

Pour exclure les motifs et juste imprimer ce qui est entre eux:

$ sed -n '/PAT1/,/PAT2/{/PAT1/{n};/PAT2/{d};p}' filename
3    - first block
4
7    - second block
10    - third block

Qui se décompose comme

  • sed -n '/PAT1/,/PAT2/ - localisez la plage entre PAT1 et PAT2 et supprimer l'impression;

  • /PAT1/{n}; - s'il correspond à PAT1 déplacer à la ligne n (suivante);

  • /PAT2/{d}; - s'il correspond à PAT2 delete line;

  • p - affiche toutes les lignes comprises dans /PAT1/,/PAT2/ et n'ont pas été ignorés ou supprimés.

1
David C. Rankin