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.