Comment puis-je obtenir «grep -zoP» pour afficher chaque correspondance séparément?
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-z
dans 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
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 -z
et (principalement) grep -P
, alors peut-être que c'est acceptable.
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+foobaz
une 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
Voici une autre solution gnu-awk utilisant RS
et 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
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 X
n'est pas un non-répétition regexp metachar (par exemple .
, ^
, [
, etc.) afin YMMV
GNU grep -z
termine 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
-onumber
utilisé au lieu de lookarounds. ?
quantificateur paresseux ajouté (au cas où cela \1
se produirait plus tard).