PCRE-regex Utiliser grep pour exclure un groupe de capture

J'utilise GNU grep avec la prise en charge de -P PCRE Regex pour faire correspondre les strings à partir d'un file. Le file d'input contient des lignes contenant des strings comme:

 FOO_1BAR.zoo.2.someSsortingng:More-RandomSsortingng (ssortingng here too): 0.45654343 

Je veux capturer les numéros 2 et 0.45654343 de la ligne ci-dessus. J'ai utilisé un regEx

 grep -Po ".zoo.\K[\d+](.*):\ (.*)$" file 

Mais cela me donne un résultat

 2.someSsortingng:More-RandomSsortingng (ssortingng here too): 0.45654343 

Je suis capable d'get le premier nombre du premier groupe de capture comme 2 , et aussi de faire correspondre un groupe de capture à la fin de la ligne. Mais je ne suis pas capable de passer les mots / lignes entre deux groupes de capture.

Je sais pertinemment que j'ai un groupe (.*) Qui capture ces mots au milieu. Ce que j'ai essayé de faire est d'inclure un autre \K pour l'ignorer

 grep -Po ".zoo.\K[\d+](.*):\K (.*)$" file 

Mais cela m'a donné seulement le deuxième groupe de capture comme 0.556984 .

Aussi avec un groupe non-capture avec la syntaxe (?:) comme

 grep -Po ".zoo.\K[\d+](?=.someSsortingng:More-RandomSsortingng (ssortingng here too)):\ (.*)$" 

Mais cela ne m'a rien donné. Qu'est-ce que j'oublie ici?

Le nom de grep vient après la command g/re/p ed . Son but principal est d'imprimer les lignes qui correspondent à une expression rationnelle. Ce n'est pas son rôle d'éditer le contenu de ces lignes. Vous avez sed (l'éditeur de stream) ou awk pour cela.

Maintenant, certaines implémentations grep , en commençant par GNU grep ajouté une option -o pour imprimer la partie appariée de chaque ligne (ce qui correspond à l'expression rationnelle, et non à ses groupes de capture). Vous avez une implémentation grep comme GNU à nouveau (avec -P ) ou pcregrep qui supporte les PCRE pour leurs expressions rationnelles.

pcregrep fait ajouté une option -o<n> pour imprimer le contenu d'un groupe de capture. Donc, vous pourriez faire:

 pcregrep -o1 -o2 --om-separator=' ' '.zoo.(\d+).*:\s+(.*)' 

Mais ici, la solution standard évidente est d'utiliser sed :

 sed -n 's/^.*\.zoo\.\([0-9]\{1,\}\).*:[[:space:]]\{1,\}/\1 /p' 

Ou si vous voulez perl regexps, utilisez perl:

 perl -lne 'print "$1 $2" if /\.zoo\.(\d+).*:\s+(.*)/' 

Avec GNU grep , si vous ne vous occupez pas des apparences sur différentes lignes, vous pouvez faire:

 $ grep -Po '\.zoo\.\K\d+|:\s+\K.*' < file 2 0.45654343 

Notez que while \K réinitialise le début de la partie appariée, cela ne signifie pas que vous pouvez vous en sortir avec les deux parties de l'alternance qui se chevauchent.

  grep -Po '.zoo. (\ K \ d + | .: \ K. )' 

ne fonctionnerait pas, tout comme echo foobar | grep -Po 'foo|foob' echo foobar | grep -Po 'foo|foob' ne fonctionnerait pas (en imprimant à la fois foo et foob ). foo|foob abord correspondre foo et ensuite grep search d'autres correspondances potentielles dans l'input après le foo , donc en commençant par le b de la bar , donc ne peut plus en find.

En haut avec grep -Po '\.zoo\.\K\d+|:\s+\K.*' , nous recherchons seulement :<spaces><anything> dans la deuxième partie de l'alternance. Cela correspond à la partie qui se trouve après .zoo.<digits> mais cela signifie aussi qu'elle findait :<spaces><anything> n'importe où dans l'input, pas seulement quand ils suivent .zoo.<digits> .

Il existe cependant un moyen de contourner ce problème en utilisant un autre opérateur spécial PCRE: \G \G correspond au début du sujet. Pour une seule correspondance, cela équivaut à ^ , mais avec plusieurs correspondances (pensez au drapeau g de sed / perl dans s/.../.../g ) comme avec -ogrep essaye de find toutes les correspondances dans la ligne, qui correspond également après la fin du match précédent. Donc, si vous le faites:

 grep -Po '\.zoo\.\K\d+|(?!^)\G.*:\s+\K.*' 

(?!^) Est un opérateur de look-ahead négatif qui ne signifie pas au début de la ligne , que \G ne correspondra qu'après un succès précédent (non vide), donc .*:\s+\K.* ne correspondra que si elle suit une correspondance réussie précédente et qui ne peut être que le .foo.<digits> car l'autre partie de l'alternance correspond à la fin de la ligne.

Sur une input comme:

 .zoo.1.zoo.2 tar: blah 

Cela donnerait:

 1 2 blah 

Bien que. Si vous ne le vouliez pas, vous souhaiteriez également que la première partie de l'alternance ne corresponde qu'au début de la ligne. Quelque chose comme

 grep -Po '^.*?\.zoo\.\K\d+|(?!^)\G.*:\s+\K.*' 

Cela produit toujours 2 sur une input comme .zoo.2 no colon character ou .zoo.2 blah: Vous pouvez travailler avec un opérateur de search anticipée dans la première partie de l'alternance et searchr au less un non-espace après :<spaces> (et aussi en utilisant $ pour éviter les problèmes avec des non-caractères)

 grep -Po '^.*?\.zoo\.\K\d+(?=.*:\s+\S.*$)|(?!^)\G.*:\s+\K\S.*$' 

Vous auriez probablement besoin de quelques pages de commentaires pour expliquer cette regexp, alors j'irais toujours pour les solutions straightfoward sed / perl