Chcę znaleźć pliki, które mają „abc” ORAZ „efg” w tej kolejności, a te dwa ciągi znajdują się w różnych wierszach tego pliku. Np .: plik z zawartością:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Powinny być dopasowane.
Odpowiedzi:
Grep nie jest wystarczający do tej operacji.
pcregrep, który znajduje się w większości współczesnych systemów Linux, może być używany jako
gdzie
-M
,--multiline
pozwól wzorom dopasować więcej niż jedną linięIstnieje również nowszy pcre2grep . Oba są dostarczane przez projekt PCRE .
pcre2grep jest dostępny dla Mac OS X poprzez porty Mac jako część portu
pcre2
:i za pośrednictwem Homebrew jako:
lub dla pcre2
pcre2grep jest również dostępny w systemie Linux (Ubuntu 18.04+)
źródło
-M, --multiline
- Zezwalaj wzorom na dopasowanie więcej niż jednej linii.'abc.*(\n|.)*?efg'
.*
->'abc(\n|.)*?efg'
skrócenie wyrażenia regularnego (i być pedantycznym)pcregrep
ułatwia rzeczy, alegrep
też działa. Na przykład patrz stackoverflow.com/a/7167115/123695Nie jestem pewien, czy jest to możliwe z grep, ale sed bardzo ułatwia:
źródło
sed
, ale jeśli nigdy wcześniej nie widziałem takiego wyrażenia.Oto rozwiązanie inspirowane tą odpowiedzią :
jeśli „abc” i „efg” mogą znajdować się w tej samej linii:
jeśli „abc” i „efg” muszą znajdować się w różnych wierszach:
Params:
-z
Traktuj dane wejściowe jako zestaw wierszy zakończonych zerowym bajtem zamiast nowego wiersza. tzn. grep traktuje dane wejściowe jako jedną dużą linię.-l
wypisz nazwę każdego pliku wejściowego, z którego normalnie wydrukowano by wyjście.(?s)
aktywuj PCRE_DOTALL, co oznacza, że „.” znajduje dowolny znak lub znak nowej linii.źródło
l
. AFAIK nie ma-1
opcji numeru .-z
opcje określają grep, aby traktować znaki nowej linii,zero byte characters
to dlaczego potrzebujemy(?s)
wyrażenia regularnego? Jeśli jest to znak inny niż nowy wiersz, nie powinien.
być w stanie dopasować go bezpośrednio?sed powinno wystarczyć, jak napisano powyżej plakat LJ,
zamiast! d możesz po prostu użyć p, aby wydrukować:
źródło
W dużej mierze polegałem na pcregrep, ale w nowszym grep nie musisz instalować pcregrep dla wielu jego funkcji. Po prostu użyj
grep -P
.W przykładzie pytania PO myślę, że następujące opcje działają dobrze, a drugi najlepiej pasuje do tego, jak rozumiem pytanie:
Skopiowałem tekst jako / tmp / test1 i usunąłem „g” i zapisałem jako / tmp / test2. Oto wynik pokazujący, że pierwszy pokazuje pasujący ciąg, a drugi pokazuje tylko nazwę pliku (typowe -o ma pokazywać dopasowanie, a typowe -l pokazuje tylko nazwę pliku). Zauważ, że „z” jest konieczne dla multilinii, a „(. | \ N)” oznacza dopasowanie „cokolwiek innego niż nowa linia” lub „nowa linia” - tj. Cokolwiek:
Aby ustalić, czy Twoja wersja jest wystarczająco nowa, uruchom
man grep
i sprawdź, czy coś podobnego do tego pojawia się u góry:To pochodzi z GNU grep 2.10.
źródło
Można to łatwo zrobić, najpierw
tr
zastępując znaki nowej linii inną postacią:Tutaj używam znaku alarmu
\a
(ASCII 7) zamiast nowego wiersza. Tego prawie nigdy nie można znaleźć w tekście igrep
można go dopasować za pomocą.
lub specjalnie do niego\a
.źródło
\0
a więc potrzebowałemgrep -a
i dopasowywałem\x00
… Pomogłeś mi uprościć!echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
jest terazecho $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
.awk one-liner:
źródło
abc
końca do końca pliku, jeśli wzorzec końcowy nie jest obecny w pliku lub brakuje ostatniego wzorca końcowego. Możesz to naprawić, ale to znacznie skomplikuje skrypt./efg/
z produkcji?Możesz to zrobić bardzo łatwo, jeśli możesz użyć Perla.
Możesz to zrobić również za pomocą jednego wyrażenia regularnego, ale wymaga to przeniesienia całej zawartości pliku do jednego ciągu, co może zająć zbyt dużo pamięci w przypadku dużych plików. Dla kompletności, oto ta metoda:
źródło
.*?
), aby uzyskać minimalne dopasowanie.Nie wiem, jak zrobiłbym to z grep, ale zrobiłbym coś takiego z awk:
Musisz jednak uważać, jak to robisz. Czy chcesz, aby wyrażenie regularne pasowało do podłańcucha czy całego słowa? dodaj odpowiednio tagi \ w. Ponadto, chociaż jest to ściśle zgodne z tym, co podałeś w przykładzie, nie działa całkiem, gdy abc pojawia się drugi raz po efg. Jeśli chcesz sobie z tym poradzić, dodaj jeśli to właściwe w / abc / case itp.
źródło
Niestety nie możesz. Z
grep
dokumentów:źródło
grep -Pz
Jeśli chcesz używać kontekstów, możesz to osiągnąć, pisząc
Spowoduje to wyświetlenie wszystkiego między „abc” i „efg”, o ile znajdują się w odległości 500 linii od siebie.
źródło
Jeśli potrzebujesz, aby oba słowa były blisko siebie, na przykład nie więcej niż 3 linie, możesz to zrobić:
Ten sam przykład, ale filtrowanie tylko plików * .txt:
A także możesz zamienić
grep
polecenie naegrep
polecenie, jeśli chcesz również znaleźć wyrażenia regularne.źródło
Kilka dni temu wydałem alternatywę grep, która obsługuje to bezpośrednio, albo poprzez dopasowanie wieloliniowe, albo przy użyciu warunków - mam nadzieję, że przyda się niektórym osobom szukającym tutaj. Tak wyglądałyby polecenia dla przykładu:
Multiline:
Warunki:
Możesz również określić, że „efg” musi podążać za „abc” w określonej liczbie wierszy:
Możesz znaleźć więcej informacji na sift-tool.org .
źródło
sift -lm 'abc.*efg' testfile
zadziałał, ponieważ dopasowanie jest zachłanne i pochłania wszystkie linie aż do ostatniegoefg
w pliku.Podczas gdy opcja sed jest najprostsza i najłatwiejsza, jednowarstwowa LJ niestety nie jest najbardziej przenośna. Ci, którzy utknęli z wersją pocisku C, będą musieli uciec od grzywki:
To niestety nie działa w bash i in.
źródło
źródło
możesz użyć polecenia grep, ponieważ nie jesteś zainteresowany sekwencją wzoru.
przykład
grep -l
znajdzie wszystkie pliki, które pasują do pierwszego wzorca, a xargs będzie grepował dla drugiego wzorca. Mam nadzieję że to pomoże.źródło
Ze srebrnym wyszukiwarką :
podobny do odpowiedzi na okaziciela dzwonka, ale zamiast niego z ag. Korzyści płynące ze srebrnej wyszukiwarki mogą tu zabłysnąć.
źródło
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
nie pasujeUżyłem tego do wyodrębnienia sekwencji fasta z pliku multi fasta przy użyciu opcji -P dla grep:
Rdzeniem wyrażenia regularnego jest to,
[^>]
co przekłada się na „nie większy niż symbol”źródło
Jako alternatywę dla odpowiedzi Balu Mohana, możliwe jest, aby wymusić kolejność wzorów przy użyciu tylko
grep
,head
itail
:Ten jednak nie jest zbyt ładny. Sformatowane bardziej czytelnie:
To wypisuje nazwy wszystkich plików, w których
"pattern2"
pojawia się po"pattern1"
, lub w przypadku gdy obie pojawiają się na tej samej linii :Wyjaśnienie
tail -n +i
- wydrukuj wszystkie wiersze poi
th, włączniegrep -n
- poprzedzać pasujące linie ich numerami liniihead -n1
- wydrukuj tylko pierwszy rządcut -d : -f 1
- wydrukuj pierwszą wyciętą kolumnę, używając:
jako separatora2>/dev/null
-tail
wyjście błędu ciszy, które występuje, jeśli$()
wyrażenie zwróci pustegrep -q
- milczgrep
i wróć natychmiast, jeśli zostanie znalezione dopasowanie, ponieważ interesuje nas tylko kod wyjściaźródło
&>
? Ja też go używam, ale nigdzie go nie udokumentowałem. BTW, dlaczego właściwie musimy tak uciszyć grep?grep -q
też nie zrobi tej sztuczki?&>
nakazuje bashowi przekierowanie zarówno standardowego wyjścia, jak i standardowego błędu, zobacz REDIRECTION w instrukcji bash. Jesteś bardzo rację, że możemy równie dobrze zrobićgrep -q ...
zamiastgrep ... &>/dev/null
, dobry połów!To też powinno działać ?!
$ARGV
zawiera nazwę bieżącego pliku podczas czytania zfile_list /s
wyszukiwań modyfikatorów w nowej linii.źródło
Plik
*.sh
jest ważny, aby zapobiec przeglądaniu katalogów. Oczywiście niektóre testy mogłyby temu zapobiec.The
wyszukuje maksymalnie 1 pasujące i zwraca (-n) numer bielizny. Jeśli znaleziono dopasowanie (test -n ...), znajdź ostatnie dopasowanie efg (znajdź wszystko i weź ostatnie z tail -n 1).
jeszcze dalej.
Ponieważ wynik jest podobny
18:foofile.sh String alf="abc";
, musimy odciąć „:” do końca linii.Powinien zwrócić wynik dodatni, jeśli ostatnie dopasowanie 2. wyrażenia minęło pierwsze dopasowanie pierwszego.
Następnie zgłaszamy nazwę pliku
echo $f
.źródło
Dlaczego nie coś prostego, takiego jak:
zwraca 0 lub dodatnią liczbę całkowitą.
egrep -o (Pokazuje tylko dopasowania, trick: wiele dopasowań w tym samym wierszu daje wynik wieloliniowy, tak jakby były w różnych wierszach)
grep -A1 abc
(wypisz abc i wiersz po nim)grep efg | wc -l
(0-n liczba linii efg znalezionych po abc w tej samej lub kolejnych liniach, wynik może być użyty w „jeśli”)grep można zmienić na egrep itp., jeśli potrzebne jest dopasowanie wzorca
źródło
Jeśli masz jakieś oszacowanie odległości między dwoma ciągami „abc” i „efg”, którego szukasz, możesz użyć:
W ten sposób pierwszy grep zwróci linię z „abc” plus # num1 linii po niej i # num2 linii po niej, a drugi grep przesieje wszystkie te, aby uzyskać „efg”. Wtedy będziesz wiedział, w których plikach pojawiają się razem.
źródło
Z ugrep wydanym kilka miesięcy temu:
To narzędzie jest wysoce zoptymalizowane pod kątem szybkości. Jest także kompatybilny z GNU / BSD / PCRE-grep.
Pamiętaj, że powinniśmy używać leniwego powtarzania
+?
, chyba że chcesz dopasować wszystkie linieefg
razem do ostatniejefg
w pliku.źródło
To powinno działać:
Jeśli jest więcej niż jedno dopasowanie, możesz je odfiltrować za pomocą grep -v
źródło