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ć.

Alex L.
źródło

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
DrYak
źródło
18

Użyj -oopcji w grep.

Na przykład:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar
Rory
źródło
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
Jozuego
źródło
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.

Kyle Brandt
źródło
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
DrYak
źródło
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

Strzałka
źródło
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
Chad Huneycutt
źródło