Nie potrzebuję całej linii, tylko dopasowanie z wyrażenia regularnego


16

Po prostu muszę uzyskać dopasowanie z wyrażenia regularnego:

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

Wyjście musi być tylko tym, co zostało dopasowane, w nawiasie.

Nie sądzę, że mogę użyć grep, ponieważ pasuje do całej linii.

Daj mi znać, jak to zrobić.

Odpowiedzi:


13

2 rzeczy:

  • Jak stwierdził @Rory, potrzebujesz -oopcji, więc drukowane jest tylko dopasowanie (zamiast całej linii)
  • Ponadto, nie możesz -Pskorzystać z wyrażeń regularnych Perla, które zawierają przydatne elementy, takie jak Spójrz przed siebie (?= ) i Spójrz za nimi (?<= ) , które szukają części, ale tak naprawdę nie pasują i nie drukują ich.

Jeśli chcesz dopasować tylko część wewnątrz parensis:

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

jeśli plik zawiera żądło /(a)5667/, grep wypisze „a”, ponieważ:

  • /(zostały znalezione przez \/\(, ale ponieważ znajdują się w tyle, (?<= ) nie są zgłaszane
  • ajest dopasowany \wi dlatego jest drukowany (z powodu -o)
  • )5667/znajdują się b < \).+\/, ale ponieważ patrzą w przyszłość, (?= ) nie są zgłaszane

18

Użyj -oopcji w grep.

Na przykład:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar

4
Dobry żal ... Czy masz pojęcie, ile razy zmagałem się z sedrereferencjami, aby to zrobić?
Insyte,

10
Opcja o grep / egrep zwraca tylko to, co pasowało do całego wyrażenia regularnego, a nie tylko to, co jest w (), o które prosił.
Kyle Brandt,

1
Jednak to i tak bardzo dobrze wiedzieć :-)
Kyle Brandt

2
@KyleBrandt: Aby dopasować tylko jedną część (np. Parenses), można zaznaczyć resztę, patrząc w przyszłość lub spojrzeć w tył: (? <=) I (? =)
DrYak

7
    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

4

Jeśli chcesz tylko tego, co jest w nawiasach, potrzebujesz czegoś, co obsługuje przechwytywanie pod dopasowań (nazwane lub numerowane grupy przechwytywania). Nie sądzę, że grep lub egrep mogą to zrobić, perl i sed mogą. Na przykład za pomocą perla:

Jeśli plik o nazwie foo ma linię, wygląda to następująco:

/adsdds      /

I robisz:

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

Litera a jest zwracana. To może nie być to, czego chcesz. Jeśli powiesz nam, co próbujesz dopasować, możesz uzyskać lepszą pomoc. 1 USD to kwota uchwycona w pierwszym zestawie nawiasów. 2 USD będzie drugim zestawem itp.


Właśnie próbowałem dopasować to, co jest w nawiasie. Wydaje się, że odpowiedzią może być przekazanie go do perla lub skryptu php.
Alex L

4

Ponieważ otagowałeś swoje pytanie jako bash oprócz powłoki , istnieje inne rozwiązanie oprócz grep :

Bash ma swój własny silnik wyrażeń regularnych od wersji 3.0, wykorzystujący =~operator, podobnie jak Perl.

teraz, biorąc pod uwagę następujący kod:

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

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Pamiętaj, że musisz wywołać go jako bashnie tylko shw celu uzyskania wszystkich rozszerzeń
  • $BASH_REMATCH da cały ciąg zgodny z całym wyrażeniem regularnym, więc <Lane>8</Lane>
  • ${BASH_REMATCH[1]} da część dopasowaną przez 1. grupę, a więc tylko 8

Drogi @DrYak, mam nadzieję, że nie analizujesz XML z regex tutaj .. :)
joonas.fi

Jest jeszcze gorzej. Analizuję straszliwą mieszankę danych XML i FASTA (które wykorzystują ten >symbol do zupełnie innych celów), które zostały wyrzucone przez oprogramowanie SANSparallel do szybkiego dostosowywania na dużą skalę . Oczywiście oba formaty są przeplatane bez przeplotu. Dlatego nie można wrzucić do tego jakiejś standardowej biblioteki XML. I używam wyrażenia regularnego Bash w tym miejscu kodu, ponieważ muszę tylko wyodrębnić kilka danych, a 2 wyrażenia regularne wykonują dla mnie zadanie znacznie lepiej niż pisanie dedykowanego analizatora składni dla tego bałaganu. #LifeInBioinformatics
DrYak

Innymi słowy: istnieje punkt, w którym wyodrębnienie 1 pojedynczej liczby jest łatwiejsze do wykonania z wyrażeniem regularnym niż taniec całego tanga XML
DrYak

Hah, mam cię! :)
joonas.fi

2

Zakładając, że plik zawiera:

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

I chcesz znaków między >i </, możesz użyć albo:

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

Wszystko wypisze ciąg „xyz”.

Jeśli chcesz uchwycić cyfry tej linii:

$ 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


Dla mnie kluczowe było uświadomienie sobie, że nie działa z sedem. Jest powód, dla którego używasz [0-9] + tam. :)
user27432

@ user27423 nie, ale POSIX klasy znaku ( bolesne czytanie , przyjemne czytanie ) zrobić: echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. W niektórych przypadkach (np. [0-9]Vs. [[:digit:]]) nie pomagają one w czytelności, w innych myślę, że tak (np. [ \t\n\r\f\v]Vs. [:space:]).
Samuel Harmer

0

Dzięki temu osiągniesz to, o co prosisz, ale nie sądzę, że tego naprawdę chcesz. Umieszczam .*przed regexem, aby zjeść wszystko przed dopasowaniem, ale jest to chciwa operacja, więc pasuje to tylko do przedostatniego \wznaku w ciągu.

Pamiętaj, że musisz uciec przed parens i +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.