Comment puis-je obtenir «grep -zoP» pour afficher chaque correspondance séparément?

Nov 23 2020

J'ai un dossier sur ce formulaire:

X/this is the first match/blabla
X-this is
the second match-

and here we have some fluff.

Et je veux extraire tout ce qui apparaît après "X" et entre les mêmes marqueurs. Donc si j'ai "X + match +", je veux obtenir "match", car il apparaît après "X" et entre le marqueur "+".

Donc, pour l'exemple de fichier donné, je voudrais avoir cette sortie:

this is the first match

et alors

this is
the second match

J'ai réussi à obtenir tout le contenu entre X suivi d'un marqueur en utilisant:

grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file

C'est:

  • grep -Po '(?<=X(.))(.|\n)+(?=\1)'pour faire correspondre X suivi de (something)qui est capturé et mis en correspondance à la fin avec (?=\1)(j'ai basé le code sur ma réponse ici ).
  • Remarque J'utilise (.|\n)pour faire correspondre n'importe quoi, y compris une nouvelle ligne, et que j'utilise également -zdans grep pour faire correspondre les nouvelles lignes également.

Donc cela fonctionne bien, le seul problème vient de l'affichage de la sortie:

$ grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file
this is the first matchthis is
the second match

Comme vous pouvez le voir, toutes les correspondances apparaissent ensemble, avec "ceci est le premier match" suivi de "ceci est le deuxième match" sans aucun séparateur. Je sais que cela vient de l'utilisation de "-z", qui traite tout le fichier comme un ensemble de lignes, chacune terminée par un octet zéro (le caractère ASCII NUL) au lieu d'une nouvelle ligne (citant "man grep").

Alors: y a-t-il un moyen d'obtenir tous ces résultats séparément?

J'ai essayé aussi dans GNU Awk:

awk 'match($0, /X(.)(\n|.*)\1/, a) {print a[1]}' file

mais même pas le (\n|.*)travaillé.

Réponses

2 tripleee Nov 23 2020 at 20:23

Le cas d'utilisation est un peu problématique, car dès que vous imprimez les correspondances, vous perdez les informations sur l'emplacement exact du séparateur. Mais si cela est acceptable, essayez de le faire xargs -r0.

grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file | xargs -r0

Ces options sont des extensions GNU, mais il en est de même grep -zet (principalement) grep -P, alors peut-être que c'est acceptable.

5 Sundeep Nov 23 2020 at 20:16

awk ne prend pas en charge les références arrière dans la définition de l'expression rationnelle.

Solutions de contournement:

$ grep -zPo '(?s)(?<=X(.)).+(?=\1)' ip.txt | tr '\0' '\n' this is the first match this is the second match # with ripgrep, which supports multiline matching $ rg -NoUP '(?s)(?<=X(.)).+(?=\1)' ip.txt
this is the first match
this is
the second match

Peut également utiliser à la (?s)X(.)\K.+(?=\1)place de (?s)(?<=X(.)).+(?=\1). Vous pouvez également utiliser ici un quantificateur non gourmand pour éviter de faire correspondre match+xyz+foobazune entréeX+match+xyz+foobaz+


Avec perl

$ perl -0777 -nE 'say $& while(/X(.)\K.+(?=\1)/sg)' ip.txt
this is the first match
this is
the second match
4 anubhava Nov 23 2020 at 22:21

Voici une autre solution gnu-awk utilisant RSet RT:

awk -v RS='X.' 'ch != "" && n=index($0, ch) { print substr($0, 1, n-1)
}
RT {
   ch = substr(RT, 2, 1)
}' file
this is the first match
this is
the second match
3 EdMorton Nov 23 2020 at 21:51

Avec GNU awk pour RS multi-caractères, RT et gensub () et sans avoir à lire le fichier entier en mémoire:

$ awk -v RS='X.' 'NR>1{print "<" gensub(end".*","",1) ">"} {end=substr(RT,2,1)}' file
<this is the first match>
<this is
the second match>

Évidemment, j'ai ajouté les "<" et ">" pour que vous puissiez voir où chaque enregistrement de sortie commence / se termine.

Ce qui précède suppose que le caractère après Xn'est pas un non-répétition regexp metachar (par exemple ., ^, [, etc.) afin YMMV

1 rowboat Nov 24 2020 at 10:09

GNU grep -ztermine les enregistrements d'entrée / sortie avec des caractères nuls (utile en conjonction avec d'autres outils tels que sort -z). pcregrep ne fera pas cela:

pcregrep -Mo2 '(?s)X(.)(.+?)\1' file

-onumberutilisé au lieu de lookarounds. ?quantificateur paresseux ajouté (au cas où cela \1se produirait plus tard).