Pokaż cały plik do dopasowania

71

grep --before-context 5 pokazuje 5 linii przed meczem.

Chcę pokazać wszystko przed meczem.
Robienie grep --before-context 99999999by działało, ale nie jest bardzo ... profesjonalne.

Jak wyświetlić cały plik do dopasowania?

Nicolas Raoul
źródło

Odpowiedzi:

95

Sed jest do tego lepszy.

Po prostu zrób:

sed '/PATTERN/q' FILE

Działa to tak:

Dla każdej linii sprawdzamy, czy pasuje /PATTERN:

  • jeśli tak, wydrukujemy go i zakończymy
  • w przeciwnym razie drukujemy to

Jest to najbardziej wydajne rozwiązanie, ponieważ gdy tylko zobaczy PATTERN, przestaje działać. Bez tego qsed nadal czytałby resztę pliku i nic z tym nie robił. W przypadku dużych plików może to mieć znaczenie.

Tej sztuczki można także użyć do emulacji head:

sed 10q FILE
Mikel
źródło
Właśnie wypróbowałem, po prostu wypisuje pierwszą linię pliku ... mimo że dopasowanie jest w linii 38.
Nicolas Raoul
Działa dla mnie dobrze. Czy możesz podać przykład rzeczywistego wejścia i wyjścia? I polecenie, które uruchamiasz w obecnej postaci.
Mikel
Wypróbowałem twoje polecenie przed jego edycją, było to: sed '/ WZORZEC / p; q' PLIK
Nicolas Raoul
7
Co powinienem zrobić, jeśli nie chcę drukować linii z dopasowanym wzorem?
tommy.carstensen
4
@ tommy.carstensen: sed '/PATTERN/Q' FILEpominie dopasowaną linię. Qjest rozszerzeniem GNU, więc nie będzie działać z żadnym sedem.
Alex O
37

sed może zastąpić większość funkcjonalności grep.

sed -n '1,/<pattern>/ p' <file>

Oznacza to drukowanie od pierwszej linii, aż wzór zostanie dopasowany.

Kilka przykładów zakresu

sed -n '/<pattern>/,$ p' <file> # from pattern to end of file
sed -n '/<pattern1>/,/<pattern2>/ p' <file> # from pattern1 to pattern2
forcefsck
źródło
3
To polecenie jest dobre, ale możesz zrobić lepiej. W ten sposób odczytuje cały plik, ale można wyjść, gdy tylko znajdzie dopasowanie.
Mikel
3
Co powinienem zrobić, jeśli nie chcę drukować linii z dopasowanym wzorem?
tommy.carstensen
34

wydrukuj do meczu włącznie:

awk '{print} /pattern/ {exit}' filename
sed '/pattern/q' filename

wydrukuj do ALE NIE w tym meczu:

awk '/pattern/ {exit} {print}' filename
sed '/pattern/Q' filename
Glenn Jackman
źródło
11
Qfajny, ale specyficzny dla GNU afaik, sed -n '/pattern/!p;//q'byłby bardziej przenośny.
don_crissti
@don_crissti: należy dokonać jej odpowiedź, myślę, że to dobry (: Jestem trochę ciekawy, jak to działa, chociaż wierzę. !sprawia, że pstosuje się do linii nie pasujących pattern, ale potem //qmyli mnie ...
JWD
2
@don_crissti: ah, rozumiem - //oznacza „poprzednie wyrażenie regularne” (myślałem, że oznacza „dopasuj pusty ciąg”). Myślę, że krótsza wersja tego samego rozwiązania to sed -n '/pattern/q;p:?
jwd
@jwd - rzeczywiście jest to krótsze. 👍
don_crissti
1

Następujące grepmetody czystego GNU nie są wydajne.

Wyszukaj wszystko do pierwszego wystąpienia ciągu „ foo ” na pasku plików , używając trzech greps:

grep -m 1 -B $(grep -n -m 1 foo bar | grep -o '^[0-9]*') foo bar

Dopasowane do ostatniej instancji „ foo ”:

grep -oPz "(?s)[^\n]*${s}.*?\n.*?foo.*?\n" bar

Uwaga: szczegółowe informacje na temat ostatniego grepmożna znaleźć w: Regex (grep) do wyszukiwania wieloliniowego .

agc
źródło
Dlaczego ktoś miałby chcieć używać 7 greps (+ pcre), gdy chodzi o proste uruchomienie pojedynczego sedwywołania: sed 'x;/./G;//!x;/foo/p;//s/.*//;x;d'??
don_crissti
@don_crissti, twój sedkod wydaje się wart własnej odpowiedzi lub może zostać dodany do jednego z pozostałych. Re 7 greps: Ponieważ nie było grepodpowiedzi ... (plus, odpowiedź pomaga pokazać, dlaczego nie.)
agc
To nie jest mój kod, po prostu kliknij go ... Nawet jeśli to był mój kod, nie odpowiada tutaj Q, więc nie opublikowałbym go jako odpowiedzi.
don_crissti
1

Dodanie do odpowiedzi Mikela powyżej ...


Aby wydrukować wszystkie wiersze do pierwszego wiersza FILEzawierającego PATTERN, ale bez uwzględnienia , spróbuj:

  • sed '/.*PATTERN.*/{s///;q;}' FILE

Pasuje do całej linii zawierającej wzorzec, zastępuje ją pustą linią, a następnie kończy pracę bez przetwarzania reszty pliku.


Postscriptum:

Najłatwiejszym / najczystszym sposobem, jaki mogłem wymyślić, aby zapobiec drukowaniu dodatkowej nowej linii na końcu (bez angażowania innego narzędzia), było ponowne uruchomienie sed i usunięcie nowej ostatniej linii:

sed '/.*PATTERN.*/{s///;q;}' FILE | sed '$d'

... a ponieważ i tak usuwamy tę linię, nasza poprzednia praca jest zbędna i możemy uprościć:

sed '/PATTERN/q' FILE | sed '$d'
Jim Grisham
źródło
Odpowiedź Glenna - i mój komentarz tam - pokazują, jak to zrobić za pomocą jednego sedwywołania.
don_crissti
(Dzięki za to - widziałem twój komentarz do odpowiedzi agc, ale albo przeoczyłem drugą, albo po prostu ją przejrzałem, ponieważ mój mózg nie lubi podwójnych negatywów.) Ponieważ użyłem tego zarówno tcshw bashaliasie, jak i aliasu, musiałem się upewnić miał stosunkowo zwięzłe jedno-liniowe rozwiązanie, które działało zarówno w standardzie, jak i GNU sed(w celu przenoszenia); wszystkie wymagania, które Twój wkład mógł bardzo dobrze spełnić. Jako ktoś, kto używa sed bardzo rzadko, moim najważniejszym wymogiem było coś, co mogłem szybko zrozumieć, kiedy chcę łatwo edytować lub zmienić przeznaczenie za lata.
Jim Grisham,
1

Dla osób, które decydują się pamiętać tylko podstawowe narzędzia w codziennej pracy i chcą zaakceptować mniej eleganckie i mniej wydajne rozwiązania:

head -n $(grep -n pattern filename | cut -d: -f1) filename

Jeśli to polecenie dotyczy skryptu, będę szukał bardziej eleganckich (i być może wydajnych) rozwiązań. Jeśli jest to jednorazowe polecenie lub skrypt wyrzucający, to mnie to nie obchodzi.

lesmana
źródło
1
Fajny pomysł, ale trzy polecenia, kiedy się da.
Mikel
1
Znajomość podstaw jest naprawdę bardzo dobra. Lepsza jest jednak znajomość odpowiedniego narzędzia do pracy.
soulmerge
Jeśli to polecenie dotyczy skryptu, będę szukał bardziej eleganckich (i być może wydajnych) rozwiązań. Jeśli jest to polecenie jednorazowe (lub skrypt wyrzucający), to mnie to nie obchodzi.
lesmana,
0

Możesz także użyć jednego z poniższych

tac ./test | grep -B $(cat ./test | wc -l) -m 1 'pattern'|tac 

lub

tac ./test |head -n $(tac ./test | grep -n 'pattern' | cut -d: -f1 | head -n 1)|tac

lub

tac ./test |sed ':a;N;$!ba;s/\n/'"pattern"'/g' | sed 's/'"patternpattern"'/\n/g'|head -n 1|sed 's/'"pattern"'/\n/g'|tac

Pierwsza opcja jest bardzo podobna do sugerowanej przez PO, tylko upewnia się, że ti pokazuje wystarczającą liczbę linii przed kontekstem, zliczając linie w pliku

Druga opcja przeszukuje numer wiersza pierwszego dopasowania (możesz to również zmienić, zmieniając wewnętrzną „głowę”), a następnie używa głowy na tym numerze

Ostatnia opcja zastępuje wszystkie nowe linie dopasowaniem, a następnie zastępuje dwa sąsiednie dopasowania nową linią. Wynikiem tego jest wiersz dla każdego bloku tekstu między dwoma dopasowaniami. Następnie używa „head”, aby wybrać pierwszy wiersz (zmieniając blok tekstu do pierwszego dopasowania), a następnie ponownie tłumaczy każde dopasowanie do nowego wiersza. ta opcja działa tylko wtedy, gdy plik ma następujący format

pattern
texttexttext
texttexttext texttexttext
texttexttexttexttexttexttexttexttext
pattern
texttexttext
pattern 
texttexttext
texttexttexttexttexttexttexttexttext

i tak dalej

użytkownik122778
źródło
2
zastanów się nad wyjaśnieniem, w jaki sposób one działają, szczególnie dlatego, że to sedpolecenie na dole jest dość srogie.
strugee,
pierwsza opcja jest bardzo podobna do sugerowanej przez OP, tylko upewnia się, że ti pokazuje wystarczającą liczbę kinesów przed kontekstem, zliczając linie w
filr