Benötigen Sie nicht die gesamte Zeile, sondern nur die Übereinstimmung mit dem regulären Ausdruck

13

Ich muss einfach die Übereinstimmung aus einem regulären Ausdruck erhalten:

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

Die Ausgabe muss nur das sein, was in der Klammer übereinstimmt.

Ich glaube nicht, dass ich grep verwenden kann, da es mit der gesamten Zeile übereinstimmt.

Bitte lassen Sie mich wissen, wie das geht.

Alex L
quelle

Antworten:

11

2 Dinge:

  • Wie von @Rory angegeben, benötigen Sie die -oOption, damit nur die Übereinstimmungen gedruckt werden (anstelle der ganzen Zeile).
  • Außerdem müssen Sie die -PMöglichkeit haben, reguläre Perl-Ausdrücke zu verwenden, die nützliche Elemente wie Look ahead (?= ) und Look behind enthalten. Diese (?<= )suchen nach Teilen, stimmen aber nicht überein und drucken sie.

Wenn Sie möchten, dass nur der Teil in der Parensis übereinstimmt:

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

Wenn die Datei den Stich enthält /(a)5667/, gibt grep 'a' aus, weil:

  • /(werden von gefunden \/\(, werden aber nicht gemeldet , weil sie sich im Blick hinter die (?<= ) Kulissen befinden
  • awird abgeglichen von \wund wird somit gedruckt (wegen -o)
  • )5667/werden b <gefunden \).+\/, aber weil sie sich im Voraus befinden, werden (?= ) sie nicht gemeldet
DrYak
quelle
17

Verwenden Sie die -oOption in grep.

Z.B:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar
Rory
quelle
4
sedMeine Güte ... Hast du eine Ahnung, wie oft ich mit Gegenreferenzen gerungen habe, um das zu tun?
Insyte
9
Die o-Option grep / egrep gibt nur das zurück, was mit dem gesamten regulären Ausdruck übereinstimmt, nicht nur das, was in () steht, wie er es verlangt hat.
Kyle Brandt
1
Allerdings ist das sowieso sehr gut zu wissen :-)
Kyle Brandt
2
@KyleBrandt: Um nur einen Teil (z. B. die Parenses) abzugleichen, können Sie den Rest mit einem Blick nach vorne oder einem Blick nach hinten markieren: (? <=) Und (? =)
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
quelle
4

Wenn Sie nur möchten, was in Klammern steht, benötigen Sie etwas, das die Erfassung von Unterübereinstimmungen (benannte oder nummerierte Erfassungsgruppen) unterstützt. Ich glaube nicht, dass grep oder egrep das können, perl und sed können. Zum Beispiel mit Perl:

Wenn eine Datei mit dem Namen foo folgende Zeile enthält:

/adsdds      /

Und du machst:

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

Der Buchstabe a wird zurückgegeben. Das ist vielleicht nicht das, was Sie wollen. Wenn Sie uns mitteilen, was Sie abgleichen möchten, erhalten Sie möglicherweise bessere Hilfe. $ 1 ist das, was im ersten Satz von Klammern erfasst wurde. $ 2 wäre der zweite Satz usw.

Kyle Brandt
quelle
Ich habe nur versucht, das in Klammern wiederzugeben. Es scheint, als ob die Weitergabe an ein Perl- oder ein PHP-Skript die Antwort wäre.
Alex L
4

Da Sie Ihre Frage zusätzlich zur Shell als bash markiert haben , gibt es neben grep noch eine andere Lösung :

Bash hat seit Version 3.0 eine eigene reguläre Ausdrucks-Engine, die den =~Operator verwendet, genau wie Perl.

jetzt mit folgendem Code:

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

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Beachten Sie, dass Sie es als bashund nicht nur aufrufen shmüssen, um alle Erweiterungen zu erhalten
  • $BASH_REMATCH gibt die gesamte Zeichenfolge an, die mit dem gesamten regulären Ausdruck übereinstimmt, also <Lane>8</Lane>
  • ${BASH_REMATCH[1]} gibt den von der 1. Gruppe abgeglichenen Teil also nur an 8
DrYak
quelle
Sehr geehrte @ DrYak, ich hoffe, Sie analysieren XML nicht mit Regex hier .. :)
joonas.fi
Es ist noch schlimmer. Ich analysiere eine schreckliche Mischung aus XML- und FASTA-Daten (die beide das >Symbol für ganz unterschiedliche Zwecke verwenden), wie sie von der schnellen SANSparallel-Ausrichtungssoftware für große Maßstäbe ausgespuckt wurden . Natürlich werden beide Formate interlaced gespuckt, ohne dass es zu einem Escape kommt. Es ist daher unmöglich, eine Standard-XML-Bibliothek darauf zu werfen. Und ich verwende an dieser Stelle des Codes Bash-Regex, weil ich nur ein paar Daten extrahieren muss und 2 Regex die Aufgabe viel besser für mich erledigen, als einen dedizierten Parser für dieses Durcheinander zu schreiben. #LifeInBioinformatics
DrYak
Mit anderen Worten: Es gibt einen Punkt, an dem es einfacher ist, eine einzelne Zahl mit einem Regex-Rathan zu extrahieren, als den gesamten XML-Tango zu tanzen
DrYak 18.06.16
Hah, gotcha! :)
joonas.fi
2

Angenommen, die Datei enthält:

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

Und Sie möchten die Zeichen zwischen >und </, können Sie entweder verwenden:

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

Alle geben einen String "xyz" aus.

Wenn Sie die Ziffern dieser Zeile erfassen möchten:

$ 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

Pfeil
quelle
Für mich war es entscheidend zu erkennen, dass \ d nicht mit sed funktioniert. Es gibt einen Grund, warum Sie dort [0-9] + verwenden. :)
user27432
0

Dies wird das erreichen, was Sie verlangen, aber ich denke nicht, dass es das ist, was Sie wirklich wollen. Ich habe die .*Regex in den Vordergrund gestellt, um vor dem Match etwas zu essen, aber das ist eine gierige Operation, daher stimmt dies nur mit dem vorletzten \wCharakter in der Zeichenfolge überein .

Beachten Sie, dass Sie die Parens und die entkommen müssen +.

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