Wydaje mi nadużywania grep
/ egrep
.
Próbowałem wyszukać ciągi w wielu wierszach i nie mogłem znaleźć dopasowania, podczas gdy wiem, że to, czego szukam, powinno pasować. Początkowo myślałem, że moje wyrażenia regularne są błędne, ale w końcu przeczytałem, że te narzędzia działają w wierszu (również moje wyrażenia regularne były tak trywialne, że nie mogło być problemu).
Którego narzędzia użyłbyś do wyszukiwania wzorców w wielu liniach?
grep
. Są ściśle powiązane, ale nie dupsami, IMO."grep"
sugerując czasownik „grep”, a najlepsze odpowiedzi, w tym akceptowane, nie używaj grep.Odpowiedzi:
Oto
sed
taki, który da cigrep
podobne zachowanie w wielu liniach:Jak to działa
-n
pomija domyślne zachowanie drukowania każdej linii/foo/{}
instruuje, aby dopasowaćfoo
i zrobić to, co jest w zawijasach do pasujących linii. Zamieńfoo
na początkową część wzoru.:start
to rozgałęziona etykieta, która pomaga nam zapętlać, dopóki nie znajdziemy końca naszego wyrażenia regularnego./bar/!{}
wykona to, co jest w zawijasach, do linii, które nie pasująbar
. Zamieńbar
na końcową część wzoru.N
dołącza następny wiersz do aktywnego bufora (sed
nazywa to przestrzenią wzorów)b start
bezwarunkowo rozgałęzi się dostart
etykiety, którą wcześniej utworzyliśmy, aby dodawać następny wiersz, dopóki przestrzeń wzorcowa nie będzie zawierałabar
./your_regex/p
drukuje przestrzeń wzoru, jeśli pasujeyour_regex
. Powinieneś zastąpićyour_regex
całym wyrażeniem, które chcesz dopasować w wielu liniach.źródło
sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
sed: unterminated {
błądsed
implementacjami. Próbowałem zastosować się do zaleceń zawartych w tej odpowiedzi, aby powyższy skrypt był zgodny ze standardami, ale powiedział mi, że „start” był niezdefiniowaną etykietą. Nie jestem więc pewien, czy można to zrobić w sposób zgodny ze standardami. Jeśli dasz radę, zredaguj moją odpowiedź.Ogólnie używam narzędzia o nazwie,
pcregrep
które można zainstalować w większości wersji linuksowych za pomocąyum
lubapt
.Na przykład
Załóżmy, że masz plik o nazwie
testfile
z zawartościąMożesz uruchomić następujące polecenie:
wykonać dopasowanie wzorca w wielu liniach.
Co więcej, możesz zrobić to samo
sed
.źródło
Oto prostsze podejście przy użyciu Perla:
lub (od JosephR wziął
sed
trasę , będę bezczelnie kraść jego sugestii )Wyjaśnienie
$f=join("",<>);
: odczytuje cały plik i zapisuje jego zawartość (znaki nowego wiersza i wszystkie) w zmiennej$f
. Następnie próbujemy dopasowaćfoo\nbar.*\n
i wydrukować go, jeśli pasuje (specjalna zmienna$&
zawiera ostatnie znalezione dopasowanie). Jest///m
to konieczne, aby wyrażenie regularne pasowało do nowych linii.-0
Ustawia separator rekordu wejściowego. Ustawienie tej opcji00
aktywuje „tryb akapitowy”, w którym Perl będzie używał kolejnych znaków nowej linii (\n\n
) jako separatora rekordów. W przypadkach, gdy nie ma kolejnych nowych wierszy, cały plik jest odczytywany (zawieszany) jednocześnie.Ostrzeżenie:
Czy nie to zrobić dla dużych plików, to załadowanie całego pliku do pamięci i że może być problem.
źródło
Jednym ze sposobów na to jest Perl. np. oto zawartość pliku o nazwie
foo
:Oto kilka Perli, które pasują do dowolnej linii rozpoczynającej się od foo, po której następuje dowolna linia rozpoczynająca się od paska:
Perl, w podziale:
while(<>){$all .= $_}
Spowoduje to załadowanie całego standardowego wejścia do zmiennej$all
while($all =~
Podczas gdy zmiennaall
ma wyrażenie regularne .../^(foo[^\n]*\nbar[^\n]*\n)/m
Wyrażenie regularne: foo na początku wiersza, po którym następuje dowolna liczba znaków innych niż nowa linia, po której następuje nowa linia, po której następuje natychmiast „kreska”, a reszta linii zawiera kreskę./m
na końcu wyrażenia regularnego oznacza „dopasuj do wielu linii”print $1
Wydrukuj część wyrażenia regularnego, która była w nawiasie (w tym przypadku całe wyrażenie regularne)s/^(foo[^\n]*\nbar[^\n]*\n)//m
Usuń pierwsze dopasowanie wyrażenia regularnego, abyśmy mogli dopasować wiele przypadków wyrażenia regularnego w danym plikuA wynik:
źródło
perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
Alternatywny sift grep obsługuje dopasowanie wielu linii (zastrzeżenie: jestem autorem).
Załóżmy, że
testfile
zawiera:sift -m '<description>.*?</description>'
(pokaż wiersze zawierające opis)Wynik:
sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename
(wyodrębnij i ponownie sformatuj opis)Wynik:
źródło
Po prostu normalne grep, który obsługuje
Perl-regexp
parametrP
, wykona tę pracę.(?s)
zwany modyfikatorem DOTALL, który sprawia, że kropka w wyrażeniu regularnym jest dopasowywana nie tylko do znaków, ale także do podziałów linii.źródło
-P
opcjiRozwiązałem ten dla mnie, używając grep i opcji -A z innym grep.
Opcja -A 1 drukuje 1 linię po znalezionej linii. Oczywiście zależy to od kombinacji plików i słów. Ale dla mnie było to najszybsze i niezawodne rozwiązanie.
źródło
Załóżmy, że mamy plik test.txt zawierający:
Można użyć następującego kodu:
Dla następujących danych wyjściowych:
źródło
Jeśli chcemy uzyskać tekst między 2 wzorcami, wyłączając siebie.
Załóżmy, że mamy plik test.txt zawierający:
Można użyć następującego kodu:
Dla następujących danych wyjściowych:
Jak to działa, zróbmy to krok po kroku
/foo/{
jest wyzwalany, gdy wiersz zawiera „foo”n
zamień przestrzeń wzoru na następny wiersz, tzn. słowo „tutaj”b gotoloop
gałąź do etykiety „gotoloop”:gotoloop
definiuje etykietę „gotoloop”/bar/!{
jeśli wzór nie zawiera „paska”h
zamień przestrzeń wstrzymania na wzór, aby „tutaj” zostało zapisane w przestrzeni wstrzymaniab loop
rozgałęzić się do etykiety „pętla”:loop
definiuje etykietę „pętla”N
dołącza wzór do przestrzeni wstrzymania.Teraz przytrzymaj miejsce zawiera:
„tutaj”
„jest”
:gotoloop
Jesteśmy teraz w kroku 4 i zapętlamy, aż linia będzie zawierać „słupek”/bar/
pętla jest zakończona, znaleziono „słupek”, to przestrzeń wzorcowag
przestrzeń wzoru jest zastępowana przestrzenią wstrzymania, która zawiera wszystkie linie między „foo” a „słupkiem”, które zostały zapisane podczas głównej pętlip
skopiuj przestrzeń wzoru na standardowe wyjścieGotowy !
sed multiline loop
źródło