Pas besoin de toute la ligne, juste la correspondance de l'expression régulière

15

J'ai simplement besoin d'obtenir le match à partir d'une expression régulière:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

La sortie doit être uniquement celle qui a été mise en correspondance, entre parenthèses.

Ne pensez pas que je peux utiliser grep car il correspond à toute la ligne.

S'il vous plait, faites moi savoir comment faire ça.

Alex L
la source

Réponses:

12

2 choses:

  • Comme indiqué par @Rory, vous avez besoin de l' -ooption, donc seule la correspondance est imprimée (au lieu de la ligne entière)
  • De plus, vous ne pouvez pas utiliser l' -Poption, pour utiliser les expressions régulières Perl, qui incluent des éléments utiles comme Regarder devant (?= ) et Regarder derrière (?<= ) , ceux-ci recherchent des pièces, mais ne les font pas correspondre et ne les impriment pas.

Si vous souhaitez que seule la partie à l'intérieur de la parensis soit mise en correspondance:

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

si le fichier contient la piqûre /(a)5667/, grep affichera 'a', car:

  • /(sont trouvés par \/\(, mais parce qu'ils sont en arrière-plan, (?<= ) ils ne sont pas signalés
  • acorrespond à \wet est donc imprimé (à cause de -o)
  • )5667/sont trouvés b < \).+\/, mais parce qu'ils sont dans une perspective d'avenir, (?= ) ils ne sont pas signalés
DrYak
la source
18

Utilisez l' -ooption dans grep.

Par exemple:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar
Rory
la source
4
Bon deuil ... Avez-vous une idée du nombre de fois où j'ai lutté contre les sedréférences pour faire ça?
Insyte
10
L'option o de grep / egrep renvoie uniquement ce qui correspond à l'expression régulière entière, pas seulement ce qui est entre () comme il l'a demandé.
Kyle Brandt
1
Cependant, c'est une très bonne chose à savoir de toute façon :-)
Kyle Brandt
2
@KyleBrandt: Pour correspondre à une seule partie (par exemple: les parences), il est possible de marquer le reste avec un regard devant ou derrière: (? <=) Et (? =)
DrYak
6
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it
Joshua
la source
4

Si vous ne voulez que ce qui est entre parenthèses, vous avez besoin de quelque chose qui prend en charge la capture de sous-correspondances (groupes de capture nommés ou numérotés). Je ne pense pas que grep ou egrep peuvent faire cela, perl et sed le peuvent. Par exemple, avec perl:

Si un fichier appelé foo a une ligne qui est comme suit:

/adsdds      /

Et vous faites:

perl -nle 'print $1 if /\/(\w).+\//' foo

La lettre a est retournée. Ce n'est peut-être pas ce que vous voulez. Si vous nous dites ce que vous essayez de faire correspondre, vous obtiendrez peut-être une meilleure aide. $ 1 est tout ce qui a été capturé dans le premier jeu de parenthèses. 2 $ serait le deuxième set, etc.

Kyle Brandt
la source
J'essayais juste de faire correspondre ce qui est entre parenthèses. On dirait que le passer à un script perl ou php pourrait être la réponse.
Alex L
4

Parce que vous avez marqué votre question comme bash en plus du shell , il existe une autre solution à côté de grep :

Bash possède son propre moteur d'expression régulière depuis la version 3.0, utilisant l' =~opérateur, tout comme Perl.

maintenant, étant donné le code suivant:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Notez que vous devez l'invoquer en tant que bashet pas seulement shafin d'obtenir toutes les extensions
  • $BASH_REMATCH donnera la chaîne entière comme correspond à l'expression régulière entière, donc <Lane>8</Lane>
  • ${BASH_REMATCH[1]} donnera la partie correspondant au 1er groupe, donc seulement 8
DrYak
la source
Cher @DrYak, j'espère que vous n'analysez pas XML avec regex ici .. :)
joonas.fi
C'est encore pire. Je suis en train d'analyser un mélange horrible de données XML et FASTA (qui utilisent toutes deux le >symbole à des fins entièrement différentes) tel que répandu par le logiciel d'alignement rapide à grande échelle SANSparallel. Bien sûr, les deux formats sont entrelacés sans échappatoire. Il est donc impossible de jeter une bibliothèque XML standard à cela. Et j'utilise Bash regex à ce stade du code car je n'ai besoin d'extraire que quelques données, et 2 regex font le travail beaucoup mieux pour moi que d'écrire un analyseur dédié pour ce gâchis. #LifeInBioinformatics
DrYak
En d'autres termes: il y a un moment où extraire un seul numéro est plus simple à faire avec un rathan regex que de danser tout le tango XML
DrYak
Hah, gotcha! :)
joonas.fi
2

En supposant que le fichier contient:

$ cat file
Text-here>xyz</more text

Et vous voulez le (s) caractère (s) entre >et </, vous pouvez utiliser soit:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

Tous afficheront une chaîne "xyz".

Si vous souhaitez capturer les chiffres de cette ligne:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file

La Flèche
la source
Pour moi, il était crucial de réaliser que \ d ne fonctionne pas avec sed. Il y a une raison pour laquelle vous utilisez [0-9] + ici. :)
user27432
@ user27423 Il ne est pas, mais les classes de caractères (POSIX lecture douloureuse , lecture agréable ) faire: echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. Dans certains cas (par exemple [0-9]vs [[:digit:]]), ils ne contribuent pas à la lisibilité, dans d'autres, je pense qu'ils le font (par exemple [ \t\n\r\f\v]vs [:space:]).
Samuel Harmer
0

Cela accomplira ce que vous demandez, mais je ne pense pas que ce soit ce que vous voulez vraiment. J'ai mis le .*devant de l'expression régulière pour manger n'importe quoi avant le match, mais c'est une opération gourmande, donc cela ne correspond qu'à l'avant-dernier \wcaractère de la chaîne.

Notez que vous devez échapper aux parens et aux +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Chad Huneycutt
la source