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?
$ 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.$ 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
.
$ 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.
$ 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.
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é.
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 finImprimer 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
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
Pour être complet, voici une solution Perl:
Perl -ne '/PAT1/../PAT2/ and print' FILE
ou:
Perl -ne 'print if /PAT1/../PAT2/' FILE
Perl -ne '/PAT1/../PAT2/ and !/PAT1/ and !/PAT2/ and print' FILE
ou:
Perl -ne 'if (/PAT1/../PAT2/) {print unless /PAT1/ or /PAT2/}' FILE
Perl -ne '/PAT1/../PAT2/ and !/PAT1/ and print' FILE
Perl -ne '/PAT1/../PAT2/ and !/PAT2/ and print' FILE
Voir également:
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.
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.
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.