Jak rekurencyjnie grepować poprzez pliki .gz?

135

Korzystam ze skryptu, aby regularnie pobierać wiadomości Gmaila, które kompresują nieprzetworzony plik .eml do plików .gz. Skrypt tworzy folder na każdy dzień, a następnie kompresuje każdą wiadomość do własnego pliku.

Chciałbym znaleźć sposób na przeszukanie tego archiwum pod kątem „ciągu”.

Wydaje się, że sam Grep tego nie robi. Próbowałem także SearchMonkey.

Kendor
źródło
16
użyć zgrep:zgrep - search possibly compressed files for a regular expression
Arkadiusz Drabczyk

Odpowiedzi:

141

Jeśli chcesz rekurencyjnie grepować we wszystkich plikach .eml.gz w bieżącym katalogu, możesz użyć:

find . -name \*.eml.gz -print0 | xargs -0 zgrep "STRING"

Musisz uciec od pierwszego, *aby powłoka go nie interpretowała. -print0każe programowi find wypisać znak zerowy po każdym znalezionym pliku; xargs -0odczytuje ze standardowego wejścia i uruchamia polecenie po nim dla każdego pliku; zgrepdziała jak grep, ale najpierw rozpakowuje plik.

JK Stafford
źródło
2
„-print0” i „-0” nie są obowiązkowe. xargs domyślnie używa „\ n”.
Jaime M.,
1
Są niezbędne, jeśli na ścieżkach mogą znajdować się spacje; nie ma innego powodu niż złożoność ich nie używać.
Daniel Griscom
2
zgrepfaktycznie wydaje się szybszy niż grepuruchamianie na nieskompresowanych plikach. Musi tak być, ponieważ skompresowane pliki można odczytać z dysku twardego i rozpakować szybciej niż odczytanie nieskompresowanego pliku z dysku twardego.
Geremia,
@JaimeM. xargswykorzystuje wykrojów (spacje) domyślnie. Jasne, pliki prawie nigdy nie mają w sobie nowych linii, ale spacje nie są niespotykane (nawet jeśli większość typów UNIXy się na nich marszczy). To powiedziawszy, możesz uprościć, nie martwiąc się o spacje jeszcze łatwiej: find . -name '*.eml.gz' -exec zgrep "STRING" {} +to dostaje tyle samo argumentów na uruchomienie xargs, bezpieczeństwo -print0/ -0, a wszystko to bez dodatkowych kosztów uruchomienia procesu i potokowania, i dość zwięźle. -execz +jest określony w POSIX, więc powinien być w większości nowszych systemów podobnych do UNIX, o ile mi wiadomo.
ShadowRanger,
@Jared Czy istnieje sposób na wyszukiwanie symboli wieloznacznych tylko na początku wzorca pliku? Na przykład mam pliki .gz, które mają na końcu znaczniki daty / godziny. ABCLog04_18_18_2_21.gz Czy istnieje sposób na rekurencyjne wyszukiwanie plików zaczynających się na ABC *. Próbowałem zastąpić \*.eml.gzw powyższym przykładzie ABCLog*komunikat o błędzie dotyczący formatu pliku .:find: paths must precede expression: ABCLog-2018-03-12-10-16-1.log.gz Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
DevelopingDeveloper
68

Jest tu wiele zamieszania, ponieważ nie ma tylko jednego zgrep. Mam dwie wersje w moim systemie, zgrepod gzipi zgrepod zutils. Ten pierwszy jest tylko skryptem otoki, który wywołuje gzip -cdfq. Nie obsługuje -r, --recursiveprzełącznika. 1
Ten ostatni jest c++programem i obsługuje-r, --recursiveopcję.
Uruchomienie zgrep --version | head -n 1ujawni, który z nich (jeśli w ogóle) jest domyślny:

zgrep (gzip) 1.6

to skrypt otoki,

zgrep (zutils) 1.3

jest cppplikiem wykonywalnym.
Jeśli masz ten drugi, możesz uruchomić:

zgrep 'pattern' -r --format=gz /path/to/dir

W każdym razie, zgodnie z sugestią, find+ zgrepbędzie działać równie dobrze z każdą wersją zgrep:

find /path/to/dir -name '*.gz' -exec zgrep -- 'pattern' {} +

Jeśli zgrepbrakuje w twoim systemie (bardzo mało prawdopodobne), możesz spróbować z:

find /path/to/dir -name '*.gz' -exec sh -c 'gzip -cd "$0" | grep -- "pattern"' {} \;

ale ma to poważną wadę: nie będziesz wiedział, gdzie są dopasowania, ponieważ do pasujących wierszy nie ma nazwy pliku.


1: ponieważ byłoby to problematyczne

don_crissti
źródło
1
jeśli zgrepz Zutils nie jest dostępny, możesz zainstalować go w Ubuntu za pomocą sudo apt-get install zutils.
therealmarv
1
Ciąg dalszy z @therealmarv ... a następnie Ubuntu użyje zgutp zutils zamiast gzip. Więc -r działa!
Elijah Lynn
Czy istnieje sposób wydrukowania numeru wiersza pliku, do którego pasuje wzór?
DogEatDog
@DogEatDog - tak jak grep -n, zgrep -nwydrukuje nr linii. Jest w instrukcji ...
don_crissti
7

agjest wariantem grepz kilkoma ładnymi dodatkowymi funkcjami.

  • ma opcję -z dla skompresowanych plików,
  • ma wiele funkcji potwierdzenia.
  • to jest szybkie

Więc:

ag -r -z your-pattern-goes-here   folder

Jeśli nie jest zainstalowany,

apt-get install silversearcher-ag   (debian and friends)
yum install the_silver_searcher     (fedora)
brew install the_silver_searcher    (mac)
JJoao
źródło
1
Dostaję ag: truncated file: Successw rezultacie. Jakąkolwiek inną flagę powinienem dodać?
Yar
4

Sama rekursja jest łatwa:

   -r, --recursive
          Read all files  under  each  directory,  recursively,  following
          symbolic  links  only  if they are on the command line.  This is
          equivalent to the -d recurse option.

   -R, --dereference-recursive
          Read all files under each directory,  recursively.   Follow  all
          symbolic links, unlike -r.

Jednak w przypadku plików skompresowanych potrzebujesz czegoś takiego:

shopt globstar 
for file in /path/to/directory/**/*gz; do zcat ""$file" | grep pattern; done

path/to/directory powinien być katalogiem nadrzędnym, który zawiera podkatalogi na każdy dzień.


zgrepjest oczywistą odpowiedzią, ale niestety nie obsługuje -rflagi. Od man zgrep:

Te opcje grep spowodują zakończenie zgrep z kodem błędu: (- [d rR zZ] | --di * | --exc * | --inc * | --rec * | --nu *).

terdon
źródło
3

Jeśli twój system ma zgrep, możesz po prostu

zgrep -irs your-pattern-goes-here the-folder-to-search-goes-here/

Jeśli twój system nie ma zgrep, możesz użyć polecenia find, aby uruchomić zcat i grep dla każdego pliku w następujący sposób:

find the-folder-to-search-goes-here/ -name '*.gz' \ -exec sh -c 'echo "Searching {}" ; zcat "{}" | grep your-pattern-goes-here ' \;

Nate z Kalamazoo
źródło
Wybacz mi zieloność tego ... pliki do przeszukania mają kilka warstw głębokości. ~ / gmvault-db / db / 2015-02 zawiera folder zarchiwizowany na każdy miesiąc, a następnie przechowuje pliki .gz dla tego miesiąca. Jeśli szukam .mil w całym tym drzewie, czy to właśnie zrobiłbym? find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Wyszukiwanie {}"; zcat "{}" | grep .mil '\;
Kendor
1
W porządku - in-r „r” spowoduje, że zgrep będzie wyszukiwał rekurencyjnie. Polecenie find działa domyślnie rekurencyjnie, więc każdy plik z rozszerzeniem .gz zostanie zapisany w zcatcie i przekazany do grep. (a {} zostanie rozwinięte do ścieżki względnej pliku, który ma zostać przeszukany). Kiedy więc trafisz, zostanie poprzedzony Searching ~/gmvault-db/db/2015-02/03/whatever.gz
Nate z Kalamazoo
Oto, co otrzymuję: find: „ścieżki muszą poprzedzać wyrażenie: -exec” Oto polecenie, którego użyłem: find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Wyszukiwanie { } "; zcat "{}" | grep .mil '\;
Kendor
usuń ukośnik odwrotny między „* .gz” i -exec.
Nate z Kalamazoo
4
zgrep-rz jakiegoś powodu nie weźmie flagi. To jest wzmianka w man zgrep(patrz także moja odpowiedź).
terdon
0

xzgrep -l "string" ./*/*.eml.gz

xzgrep jest pochodną narzędzia zgrep (mniej / bin / xzgrep)

Ze strony Man:

xzgrep wywołuje grep (1) na plikach, które mogą być nieskompresowane lub skompresowane za pomocą xz (1), lzma (1), gzip (1), bzip2 (1) lub lzop (1). Wszystkie określone opcje są przekazywane bezpośrednio do grep (1).

-l wypisz pasującą nazwę pliku

-R rekurencja nie będzie działać, ponieważ jest to wyraźnie zabronione w skrypcie, jednak proste globowanie powłoki powinno nas tam doprowadzić

./*/*.eml.gz

ze ścieżki względnej, gdzie ./today/sample.eml.gz, dopasuj we wszystkich instancjach o jeden poziom poniżej naszej względnej pozycji w powłoce, która kończy się na „.eml.gz”

Jan
źródło