Prawidłowe wyrażenie regularne nie działa w grep

13

Mam ten regex:

(?<=prefix).*$

który zwraca dowolny znak następujący po „prefiksie” ciągu i działa dobrze na dowolnych silnikach wyrażeń regularnych online (np. https://regex101.com ). Problem polega na tym, że używam tego wyrażenia regularnego w bash:

grep '(?<=prefix).*$' <<< prefixSTRING

nic nie pasuje. Dlaczego to wyrażenie regularne nie działa z grep?

mark009
źródło
11
To naprawdę podkreśla, dlaczego regex101 potrzebuje selektora smaku POSIX, tak jak robi to w JS, Perl / PHP i Python. Nie mogę policzyć, ile razy marzyłem o tym.
Jared Smith
Ponadto .*$dopasowuje dowolny ciąg znaków do końca linii (lub końca łańcucha), a nie tylko jeden znak.
ilkkachu

Odpowiedzi:

38

Wygląda na to, że zdefiniowałeś właściwe wyrażenie regularne, ale nie ustawiłeś wystarczającej liczby flag w wierszu poleceń, grepaby je zrozumieć. Ponieważ domyślnie grepobsługuje BRE, a z -Eflagą robi to ERE. To, co masz (przewidywania), jest dostępne tylko w smaku regularnym PCRE, który jest obsługiwany tylko w GNU grepz jego -Pflagą.

Zakładając, że musisz wyodrębnić tylko pasujący ciąg po prefixtym, jak musisz dodać dodatkową flagę, -oaby poinformować, grepże wypisuje tylko pasującą część jako

grep -oP '(?<=prefix).*$' <<< prefixSTRING

Istnieje również wersja, grepktóra domyślnie obsługuje biblioteki PCRE - pcregrepw której możesz to zrobić

pcregrep -o '(?<=prefix).*$' <<< prefixSTRING

Szczegółowe wyjaśnienie różnych smaków wyrażeń regularnych wyjaśniono w tej cudownej odpowiedzi Gilesa i narzędziach, które implementują każdy z nich

Inian
źródło
38

Wyrażenia regularne występują w wielu różnych smakach. To, co wyświetlasz, to wyrażenie regularne podobne do Perla (PCRE, „wyrażenie regularne zgodne z Perlem”).

greprobi wyrażenia regularne POSIX. Są to podstawowe wyrażenia regularne (BRE) i rozszerzone wyrażenia regularne (ERE, jeśli grepjest używane z -Eopcją). Zobacz instrukcję re_formatlub regexpodobną instrukcję, grepdo której odnosi się ten podręcznik w twoim systemie, lub standardowe teksty POSIX, z którymi właśnie się odniosłem.

Jeśli użyjesz GNU grep, będziesz mógł używać wyrażeń regularnych podobnych do Perla, jeśli użyjesz grepz opcją grepspecyficzną dla GNU -P.

Zauważ też, że domyślnie grepzwraca linie , a nie podciągi z linii. Ponownie, z GNU grep(i niektórymi innymi grepimplementacjami), możesz użyć tej -oopcji, aby uzyskać tylko te bity, które pasują do podanego wyrażenia z każdej linii.

Należy zauważyć, że zarówno -Pi -osą niestandardowe rozszerzenia specyfikacji POSIX zgrep .

Jeśli nie używasz GNU grep, możesz sedzamiast tego użyć bitu między ciągiem prefixa końcem wiersza:

sed -n 's/.*prefix\(.*\)/\1/p' file

Powoduje to jedynie wydrukowanie wierszy, którym sedudaje się zastosować dane podstawienie. Podstawienie zastąpi całą linię pasującą do wyrażenia (którym jest BRE) fragmentem występującym po ciągu prefix.

Zauważ, że jeśli jest kilka wystąpień prefixw linii, sedodmiana zwróci ciąg po ostatniej , podczas gdy grepodmiana GNU zwróci ciąg po pierwszej (która obejmie inne wystąpienia prefix).

sedRozwiązaniem byłoby przenośny do wszystkich systemów uniksowych.

Kusalananda
źródło
6

Jak greppodają inne odpowiedzi, nie używa smaku regularnego z lookbehinds (domyślnie z GNU greplub wcale z innymi wersjami).

Jeśli nie możesz używać GNU greplub pcregrepmożesz go użyć, perljeśli go posiadasz.

Odpowiednikiem wiersza polecenia perlbyłoby:

perl -ne 'print if /(?<=prefix).*$/' <<< prefixSTRING

Umieszczasz pożądany regex pomiędzy ukośnikami. Kiedy używasz Perla, używa to wyrażenia regularnego Perla .

kwant
źródło
lub print "$&\n" if ...jeśli chcą prefix
wydać