Powiedz, że mam plik:
# file: 'test.txt'
foobar bash 1
bash
foobar happy
foobar
Chcę tylko wiedzieć, jakie słowa pojawiają się po „foobar”, więc mogę użyć tego wyrażenia regularnego:
"foobar \(\w\+\)"
Nawiasy wskazują, że szczególnie interesuję się tym słowem zaraz po foobar. Ale kiedy robię a grep "foobar \(\w\+\)" test.txt
, otrzymuję całe wiersze, które pasują do całego wyrażenia regularnego, a nie tylko „słowo po foobar”:
foobar bash 1
foobar happy
Wolałbym, aby wynik tego polecenia wyglądał następująco:
bash
happy
Czy istnieje sposób, aby powiedzieć grepowi, aby wyświetlał tylko elementy pasujące do grupy (lub określonej grupy) w wyrażeniu regularnym?
text-processing
grep
regular-expression
Cory Klein
źródło
źródło
perl -lne 'print $1 if /foobar (\w+)/' < test.txt
Odpowiedzi:
GNU grep ma
-P
opcję dla wyrażeń regularnych w stylu perla i-o
opcję drukowania tylko tego, co pasuje do wzorca. Można je łączyć za pomocą asertywnych stwierdzeń (opisanych w części Rozszerzone wzorce na stronie podręcznika perlre ), aby usunąć część wzoru grep z tego, co do którego stwierdzono, że jest dopasowane-o
.Jest
\K
to krótka (i bardziej wydajna) forma,(?<=pattern)
której używasz jako asertywnego potwierdzenia zerowej szerokości przed tekstem, który chcesz wydrukować.(?=pattern)
może być używany jako asertywne stwierdzenie o zerowej szerokości po tekście, który chcesz wydrukować.Na przykład, jeśli chcesz dopasować słowo pomiędzy
foo
ibar
, możesz użyć:lub (dla symetrii)
źródło
sed(1)
grep -oP 'foobar \K\w+' test.txt
nic nie wychodzi z POtest.txt
. Wersja grep to 2.5.1. Co może być nie tak? O_OStandardowy grep nie może tego zrobić, ale najnowsze wersje GNU grep mogą . Możesz zmienić tryb na sed, awk lub perl. Oto kilka przykładów, które robią, co chcesz na przykładowych danych wejściowych; zachowują się nieco inaczej w przypadkach narożnych.
Zamień
foobar word other stuff
naword
, drukuj tylko po dokonaniu wymiany.Jeśli pierwszym słowem jest
foobar
, wydrukuj drugie słowo.Usuń,
foobar
jeśli jest to pierwsze słowo, w przeciwnym razie pomiń wiersz; następnie usuń wszystko po pierwszej spacji i wydrukuj.źródło
grep
. Ale składnia tych poleceń wygląda teraz bardzo znajomo, ponieważ jestem zaznajomiony z wyszukiwaniem i zastępowaniem + wyrażeń regularnych w stylu vim. Wielkie dzięki.grep
nie obsługuje PCRE.źródło
^
i$
są obce, ponieważ.*
jest chciwy mecz. Jednak włączenie ich może pomóc wyjaśnić cel wyrażenia regularnego.Cóż, jeśli wiesz, że foobar jest zawsze pierwszym słowem lub linią, możesz użyć cut. Tak jak:
źródło
-o
Włącz grep jest powszechnie wdrażane (moreso niż rozszerzeniach GNU grep), więc robigrep -o "foobar" test.file | cut -d" " -f2
zwiększy skuteczność tego rozwiązania, które jest bardziej mobilny niż przy użyciu lookbehind twierdzeń.grep -o "foobar .*
"lubgrep -o "foobar \w+"
.Jeśli PCRE nie jest obsługiwane, możesz osiągnąć ten sam wynik za pomocą dwóch wywołań grep. Na przykład, aby złapać słowo po foobar, wykonaj następujące czynności:
Można go rozwinąć do dowolnego słowa po foobar, takiego jak ten (z ERE dla czytelności):
Wynik:
Zauważ, że indeks
i
jest zerowy.źródło
pcregrep
ma inteligentniejszą-o
opcję, która pozwala wybrać grupy przechwytywania, które chcesz wydrukować. Korzystając z pliku przykładowego,źródło
Korzystanie
grep
nie jest kompatybilne z wieloma platformami, ponieważ-P
/--perl-regexp
jest dostępne tylko na GNUgrep
, a nie na BSDgrep
.Oto rozwiązanie wykorzystujące
ripgrep
:Zgodnie z
man rg
:Powiązane: GH-462 .
źródło
Odpowiedź @jgshawkey była dla mnie bardzo pomocna.
grep
nie jest tak dobrym narzędziem do tego, ale sed jest, chociaż tutaj mamy przykład, który używa grep, aby uchwycić odpowiednią linię.Składnia regex sed jest idiosynkratyczna, jeśli nie jesteś do tego przyzwyczajony.
Oto inny przykład: ten analizuje dane wyjściowe xinput, aby uzyskać liczbę całkowitą ID
i chcę 19
Zwróć uwagę na składnię klas:
i potrzeba ucieczki przed następującymi
+
Zakładam, że pasuje tylko jedna linia.
źródło
grep
, zakładając, że „TouchPad” znajduje się po lewej stronie „id”:echo "SynPS/2 Synaptics TouchPad id=19 [slave pointer (2)]" | sed -nE "s/.*TouchPad.+id=([0-9]+).*/\1/p"