web-dev-qa-db-fra.com

Comment sélectionner des lignes entre deux motifs de marqueur pouvant se produire plusieurs fois avec awk/sed

En utilisant awk ou sed, comment puis-je sélectionner des lignes apparaissant entre deux motifs de marqueur différents? Il peut y avoir plusieurs sections marquées avec ces motifs.

Par exemple: Supposons que le fichier contienne: 

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

Et le motif de départ est abc et le motif de fin est mno Alors, j'ai besoin de la sortie en tant que: 

def1
ghi1
jkl1
def2
ghi2
jkl2

J'utilise sed pour faire correspondre le motif une fois: 

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

Existe-t-il un moyen dans sed ou awk de le faire de manière répétée jusqu'à la fin du fichier? 

103
dvai

Utilisez awk avec un drapeau pour déclencher l'impression lorsque cela est nécessaire:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

Comment cela marche-t-il?

  • /abc/ correspond aux lignes ayant ce texte, de même que /mno/
  • /abc/{flag=1;next} définit la flag lorsque le texte abc est trouvé. Ensuite, il saute la ligne. 
  • /mno/{flag=0} désactive la flag lorsque le texte mno est trouvé.
  • La dernière flag est un motif avec l'action par défaut, qui consiste à print $0: si flag est égal à 1, la ligne est imprimée.

Pour une description plus détaillée et des exemples, ainsi que pour les cas où les motifs sont affichés ou non, voir Comment sélectionner des lignes entre deux motifs? .

161
fedorqui

Utiliser sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

L'option -n signifie que vous n'imprimez pas par défaut.

Le modèle recherche les lignes ne contenant que abc à juste mno, puis exécute les actions dans le { ... }. La première action supprime la ligne abc; la seconde la ligne mno; et la p imprime les lignes restantes. Vous pouvez relâcher les expressions rationnelles selon vos besoins. Toutes les lignes en dehors de la gamme abc..mno ne sont tout simplement pas imprimées.

38
Jonathan Leffler

Cela pourrait fonctionner pour vous (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

Supprimer toutes les lignes sauf celles entre les lignes commençant par abc et mno

17
potong
sed '/^abc$/,/^mno$/!d;//d' file

golfs deux caractères meilleurs que ppotong -{//!b};d

Les barres obliques vides // signifient: "réutilise la dernière expression régulière utilisée". et la commande fait la même chose que la plus compréhensible:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

Ceci semble être POSIX :

Si une RE est vide (c'est-à-dire qu'aucun modèle n'est spécifié), sed se comportera comme si la dernière RE utilisée dans la dernière commande appliquée (soit en tant qu'adresse, soit en tant que partie d'une commande de substitution) avait été spécifiée.

Parmi les liens de la réponse précédente, celui qui l'a fait pour moi, sous ksh sous Solaris, était le suivant:

sed '1,/firstmatch/d;/secondmatch/,$d'
4
FanDeLaU

La réponse de Don_crissti à partir de Afficher uniquement le texte entre 2 motifs correspondants ?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

ce qui est beaucoup plus efficace que l'application AWK, voir ici .

quelque chose comme ça marche pour moi:

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

en utilisant: awk -f file.awk data...

edit: O_o fedorqui solution est bien mieux/plus jolie que la mienne.

1
pataluc
Perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file
1
Vijay

J'ai essayé d'utiliser awk pour imprimer des lignes entre deux modèles tandis que pattern2 correspond également à pattern1 . Et la ligne pattern1 devrait également être imprimée. 

par exemple . source 

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

devrait avoir une sortie de

package BBB
ddd
eee

Où pattern1 est package BBB, pattern2 est package \w*. Notez que CCC n'est pas une valeur connue et ne peut donc pas correspondre littéralement.

Dans ce cas, ni le awk '/abc/{a=1}/mno/{print;a=0}a' file de @scai ni le awk '/abc/{a=1} a; /mno/{a=0}' file de @fedorqui ne fonctionnent pour moi.

Finalement, j'ai réussi à le résoudre par awk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' file, haha

Un peu plus d'effort résulte en awk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' file, pour imprimer aussi la ligne pattern2, c'est-à-dire

package BBB
ddd
eee
package CCC
0
Weekend