Wie greife ich rekursiv durch .gz-Dateien?

135

Ich verwende ein Skript, um meine Google Mail-Nachrichten regelmäßig herunterzuladen, mit dem die RAW-EML in GZ-Dateien komprimiert wird. Das Skript erstellt für jeden Tag einen Ordner und komprimiert dann jede Nachricht in eine eigene Datei.

Ich möchte in diesem Archiv nach einem "String" suchen.

Grep allein scheint es nicht zu tun. Ich habe auch SearchMonkey ausprobiert.

Kendor
quelle
16
Verwenden Sie zgrep:zgrep - search possibly compressed files for a regular expression
Arkadiusz Drabczyk

Antworten:

141

Wenn Sie alle .eml.gz-Dateien im aktuellen Verzeichnis rekursiv bearbeiten möchten, können Sie Folgendes verwenden:

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

Sie müssen zuerst das *Escapezeichen setzen, damit die Shell es nicht interpretiert. -print0weist find an, nach jeder gefundenen Datei ein Nullzeichen zu drucken; xargs -0Liest von der Standardeingabe und führt den Befehl danach für jede Datei aus. zgrepfunktioniert wie grep, dekomprimiert aber zuerst die Datei.

JK Stafford
quelle
2
'-print0' und '-0' sind nicht obligatorisch. xargs verwendet standardmäßig '\ n'.
Jaime M.
1
Sie sind erforderlich, wenn die Pfade Leerzeichen enthalten. Es gibt keinen anderen Grund als die Komplexität, sie nicht zu verwenden.
Daniel Griscom
2
zgrepscheint tatsächlich schneller als grepauf unkomprimierten Dateien ausgeführt. Dies muss daran liegen, dass komprimierte Dateien schneller von der Festplatte gelesen und dekomprimiert werden können als nicht komprimierte Dateien von der Festplatte.
Geremia
@JaimeM. xargsverwendet standardmäßig Leerzeichen (Whitespace). Sicher, in Dateien befinden sich fast nie Zeilenumbrüche, aber Leerzeichen sind keine Seltenheit (selbst wenn die meisten UNIXy-Typen sie verachten). Das heißt, Sie können noch einfacher vereinfachen, ohne sich um Whitespace kümmern zu müssen: find . -name '*.eml.gz' -exec zgrep "STRING" {} +So erhalten Sie die gleichen Argumente pro Start xargs, die Sicherheit von -print0/ -0und das alles, ohne den Aufwand für einen zusätzlichen Prozessstart und Piping, und das ziemlich präzise. -execmit +ist POSIX angegeben, daher sollte es meines Wissens nach auf den meisten neueren UNIX-ähnlichen Systemen sein.
ShadowRanger
@Jared Gibt es eine Möglichkeit, eine Platzhaltersuche durchzuführen, wenn nur der Anfang des Dateimusters bekannt ist? Zum Beispiel habe ich .gz-Dateien, an deren Ende Datums- / Zeitstempel stehen. ABCLog04_18_18_2_21.gz Gibt es eine Möglichkeit, rekursiv nach Dateien zu suchen, die mit ABC * beginnen? Ich habe versucht, \*.eml.gzin Ihrem obigen Beispiel durch zu ersetzen ABCLog*und erhalte eine Fehlermeldung zum Dateiformat: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

Hier herrscht viel Verwirrung, weil es nicht nur eine gibt zgrep. Ich habe zwei Versionen auf meinem System, zgrepvon gzipund zgrepvon zutils. Ersteres ist nur ein Wrapper-Skript, das aufgerufen wird gzip -cdfq. Der -r, --recursiveSchalter wird nicht unterstützt . 1
Letzteres ist ein c++Programm und unterstützt die -r, --recursiveOption.
Beim Laufen zgrep --version | head -n 1wird angezeigt, welche davon (falls vorhanden) die Standardeinstellung ist:

zgrep (gzip) 1.6

ist das Wrapper-Skript,

zgrep (zutils) 1.3

ist die cppausführbare Datei.
Wenn Sie Letzteres haben, können Sie Folgendes ausführen:

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

Auf jeden Fall funktioniert find+ , wie vorgeschlagen, zgrepmit beiden Versionen von zgrep:

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

Wenn zgrepin Ihrem System etwas fehlt (höchst unwahrscheinlich), können Sie Folgendes versuchen:

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

Aber es gibt einen großen Nachteil: Sie werden nicht wissen, wo die Übereinstimmungen sind, da den übereinstimmenden Zeilen kein Dateiname vorangestellt ist.


1: weil es problematisch wäre

don_crissti
quelle
1
wenn zgrepvon zutils nicht verfügbar ist kannst du es mit in Ubuntu installieren sudo apt-get install zutils.
Therealmarv
1
Fortsetzung von @therealmarv ... und dann verwendet Ubuntu das zutils zgrep anstelle des gzip one. Dann funktioniert -r!
Elijah Lynn
Gibt es eine Möglichkeit, die Zeilennummer der Datei zu drucken, auf die das Muster passt?
DogEatDog
@DogEatDog - wie grep -n, zgrep -nwird Zeile no.s. drucken Es steht im Handbuch ...
don_crissti
7

agist eine Variante von grep, mit ein paar netten Extras.

  • hat die Option -z für komprimierte Dateien,
  • hat viele ack Funktionen.
  • es ist schnell

Damit:

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

Wenn nicht installiert,

apt-get install silversearcher-ag   (debian and friends)
yum install the_silver_searcher     (fedora)
brew install the_silver_searcher    (mac)
Joao
quelle
1
Ich bekomme ag: truncated file: Successals Ergebnis. Soll ich eine andere Flagge hinzufügen?
Yar
4

Rekursion allein ist einfach:

   -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.

Für komprimierte Dateien benötigen Sie jedoch Folgendes:

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

path/to/directory sollte das übergeordnete Verzeichnis sein, das die Unterverzeichnisse für jeden Tag enthält.


zgrepist die offensichtliche Antwort, aber leider wird die -rFlagge nicht unterstützt . Von man zgrep:

Diese grep-Optionen führen dazu, dass zgrep mit einem Fehlercode beendet wird: (- [d rR zZ] | --di * | --exc * | --inc * | --rec * | --nu *).

terdon
quelle
3

Wenn Ihr System über zgrep verfügt, können Sie dies einfach tun

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

Wenn Ihr System nicht über zgrep verfügt, können Sie den Befehl find verwenden , um zcat und grep für jede Datei wie folgt auszuführen:

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

Nate aus Kalamazoo
quelle
Verzeihen Sie mir diesbezüglich, dass die zu durchsuchenden Dateien mehrere Ebenen tief sind. ~ / gmvault-db / db / 2015-02 enthält einen Ordner für jeden archivierten Monat und darunter werden die .gz-Dateien für diesen Monat gespeichert. Wenn ich in diesem ganzen Baum nach .mil suche, ist es das, was ich tun würde? find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Searching {}"; zcat "{}" | grep .mil '\;
Kendor
1
Das ist in Ordnung - das "r" in -irs veranlasst zgrep, rekursiv zu suchen. Der Befehl find wird standardmäßig rekursiv ausgeführt, sodass alle Dateien, die auf .gz enden, zcattiert und an grep übergeben werden. (und das {} wird auf den relativen Pfad der zu durchsuchenden Datei erweitert). Wenn Sie also einen Treffer erhalten, geht ihmSearching ~/gmvault-db/db/2015-02/03/whatever.gz
Nate von Kalamazoo,
Folgendes erhalte ich zurück: find: "Pfade müssen vor expression: -exec stehen" Hier ist der Befehl, den ich verwendet habe: find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo " } "; zcat "{}" | grep .mil '\;
Kendor
Entfernen Sie den Backslash zwischen '* .gz' und -exec.
Nate aus Kalamazoo
4
zgrepwird die -rFlagge aus irgendeinem Grund nicht nehmen . Das ist erwähnt in man zgrep(siehe auch meine Antwort).
terdon
0

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

xzgrep ist eine Ableitung der zgrep-Utils (less / bin / xzgrep)

Von der Manpage:

xzgrep ruft grep (1) für Dateien auf, die entweder nicht komprimiert oder mit xz (1), lzma (1), gzip (1), bzip2 (1) oder lzop (1) komprimiert wurden. Alle angegebenen Optionen werden direkt an grep (1) übergeben.

-l gibt den passenden Dateinamen aus

-R für die Rekursion funktioniert nicht, da dies im Skript ausdrücklich verboten ist. Ein einfaches Shell-Globbing sollte uns jedoch dahin bringen

./*/*.eml.gz

Von einem relativen Pfad aus, in dem ./today/sample.eml.gz auf allen Instanzen übereinstimmt, die eine Ebene unter unserer relativen Position in der Shell liegen und mit ".eml.gz" endet.

John
quelle