Jak zrobić niechciany mecz w grep?

Odpowiedzi:

276

Szukasz niechcianego (lub leniwego) meczu. Aby uzyskać niechciane dopasowanie w wyrażeniach regularnych, musisz użyć modyfikatora ?po kwantyfikatorze. Na przykład możesz zmienić .*na .*?.

Domyślnie grepnie obsługuje niechcianych modyfikatorów, ale możesz użyć grep -Pskładni Perla.

Mark Byers
źródło
3
eegg: dot all modyfikator jest również znany jako multiline. To modyfikator, który zmienia znak „.” dopasuj zachowanie, aby uwzględnić znaki nowej linii (normalnie nie). Nie ma takiego modyfikatora w grep, ale jest w pcregrep .
A. Wilson
1
Korekta: W większości typów wyrażeń regularnych, które go obsługują, tryb, który pozwala .dopasować znaki nowej linii, nazywa się DOTALL lub trybem jednowierszowym ; Ruby jest jedynym, który nazywa to multilinią . W innych wariantach multiline to tryb, który pozwala kotwicom ( ^i $) dopasować się na granicach linii. Ruby nie ma równoważnego trybu, ponieważ w Rubim zawsze działa w ten sposób.
Alan Moore
5
-Pbył dla mnie zupełnie nowy, szczęśliwie uciekam od lat i wykorzystuję tylko -E... tak wiele zmarnowanych lat! - Notatka dla siebie: czytaj ponownie strony podręcznika jako (nawet bardziej!) Normalną czynność, nigdy nie przyswajasz wystarczającej liczby przełączników i opcji.
ocodo
29
Na niektórych platformach (takich jak Mac OS X) grepnie obsługuje -P, ale jeśli używasz egrep, możesz użyć .*?wzorca, aby osiągnąć ten sam efekt. egrep -o 'start.*?end' text.html
SaltyNuts
4
Jako rozszerzenie komentarza @SaltyNuts, Mac OS X nie obsługuje, -Pale -Ewywołuje, egrepdlatego sugerowane .*?działa dobrze.
Fredrik Erlandsson
83

Właściwie .*?jedyny działa w perl. Nie jestem pewien, jaka byłaby równoważna składnia rozszerzonych wyrażeń regularnych grep. Na szczęście możesz użyć składni perl z grep, więc grep -Pzadziała, ale grep -Ektóra jest taka sama, jak egrepnie zadziała (byłby chciwy).

Zobacz też: http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html

John Smith
źródło
9
grep -Pnie działa w GNU grep 2.9 - tylko próbował go (nie robi błędów, tak cicho nie stosować ?Intertestly nie robi. nie klasy np:env|grep '[^\=]*\='
Roberto Tomás
2
W Darwin / OS X 10.8 Mountain Lion nie ma grep -Popcji ani pgreppolecenia, ale egrepdziała świetnie.
Steve HHH
2
Na pgrepmoim pudełku z OS X 10.9 jest polecenie, ale jest to zupełnie inny program, którego celem jest „znajdowanie lub sygnalizowanie procesów według nazwy”.
Desty,
@ robertotomás Odpowiadając na komentarz od 6-latka tutaj, ale ... Myślałem o tym, a potem zdałem sobie sprawę, że otrzymuję wiele niechcianych dopasowań. Na przykład, na kolorowym terminalu możesz zobaczyć, że `echo" bbbbb "| grep -P 'b. *? b'` zwraca 2 dopasowania.
zzxyz
12

Mój grep, który działa po wypróbowaniu rzeczy w tym wątku:

echo "hi how are you " | grep -shoP ".*? "

Tylko pamiętaj, aby dodać spację do każdego wiersza

(Mój był wiersz po wierszu wyszukiwania, aby wypluć słowa)

jonz
źródło
3
-shoPfajny mnemonik :)
Mariusz
echo "bbbbb" | grep -shoP 'b.*?b'to trochę pouczające doświadczenie. Jedyna rzecz, która zadziałała dla mnie również w kategoriach wyraźnego lenistwa.
zzxyz
12

grep

Aby nie być chciwym, grepmożesz użyć zanegowanej klasy znaków. Innymi słowy, staraj się unikać symboli wieloznacznych.

Na przykład, aby pobrać wszystkie linki do plików jpeg z zawartości strony, użyjesz:

grep -o '"[^" ]\+.jpg"'

Aby poradzić sobie z wieloma liniami, xargsnajpierw przepuść wejście . Aby uzyskać wydajność, użyj ripgrep.

kenorb
źródło
3

Krótka odpowiedź to użycie następnego wyrażenia regularnego:

(?s)<car .*? model=BMW .*?>.*?</car>
  • (? s) - to dopasowuje do multilinii
  • . *? - dopasowuje dowolny znak, kilka razy w sposób leniwy (minimalne dopasowanie)

(Nieco) bardziej skomplikowana odpowiedź brzmi:

(?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>

Umożliwi to dopasowanie car1 i car2 w poniższym tekście

<car1 ... model=BMW ...>
...
...
...
</car1>
<car2 ... model=BMW ...>
...
...
...
</car2>
  • (..) reprezentuje grupę przechwytującą
  • \ 1 w tym kontekście odpowiada temu samemu tekstowi, który został ostatnio dopasowany poprzez przechwytywanie grupy numer 1
jmc
źródło
1

Przepraszam, że spóźniłem się 9 lat, ale może to zadziałać dla widzów w 2020 roku.

Załóżmy więc, że masz taką linię "Hello my name is Jello". Teraz chcesz znaleźć słowa, które zaczynają się 'H'i kończą na 'o', z dowolną liczbą znaków pomiędzy nimi. I nie chcemy wersetów, chcemy tylko słów. W tym celu możemy użyć wyrażenia:

grep "H[^ ]*o" file

To zwróci wszystkie słowa. Sposób, w jaki to działa, jest następujący: Pozwoli to na wszystkie znaki zamiast spacji pomiędzy, w ten sposób możemy uniknąć wielu słów w tej samej linii.

Teraz możesz zastąpić spację dowolnym innym znakiem. Załóżmy, że początkowy wiersz brzmiał "Hello-my-name-is-Jello", wtedy możesz uzyskać słowa za pomocą wyrażenia:

grep "H[^-]*o" file
mr.1n5an_e
źródło
0

Wiem, że to trochę martwy punkt, ale właśnie zauważyłem, że to działa. Usunięto zarówno czyszczenie, jak i porządkowanie z mojego wyniku.

> grep -v -e 'clean\-\?up'
> grep --version grep (GNU grep) 2.20
user200850
źródło