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.


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


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)


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.


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

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

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