Come posso eseguire grep in modo ricorsivo attraverso i file .gz?


135

Sto usando uno script per scaricare regolarmente i miei messaggi di Gmail che comprime i file .eml non elaborati in file .gz. Lo script crea una cartella per ogni giorno, quindi comprime ogni messaggio nel proprio file.

Vorrei un modo per cercare in questo archivio una "stringa".

Grep da solo non sembra farlo. Ho anche provato SearchMonkey.


16
uso zgrep:zgrep - search possibly compressed files for a regular expression
Arkadiusz Drabczyk

Risposte:


141

Se vuoi eseguire grep in modo ricorsivo in tutti i file .eml.gz nella directory corrente, puoi usare:

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

Devi scappare dal primo in *modo che la shell non lo interpreti. -print0dice a find di stampare un carattere null dopo ogni file trovato; xargs -0legge dallo standard input ed esegue il comando dopo di esso per ogni file; zgrepfunziona come grep, ma prima decomprime il file.


2
'-print0' e '-0' non sono obbligatori. xargs usa '\ n' per impostazione predefinita.
Jaime M.

1
Sono necessari se potrebbero esserci caratteri spaziali nei percorsi; non c'è altro motivo che la complessità per non usarli.
Daniel Griscom,

2
zgrepin realtà sembra più veloce grepdell'esecuzione su file non compressi. Deve essere perché i file compressi possono essere letti dall'HD e decompressi più velocemente rispetto alla lettura di un file non compresso dall'HD.
Geremia,

@JaimeM. xargsutilizza gli spazi (spazio bianco) per impostazione predefinita. Certo, i file non hanno quasi mai nuove linee, ma gli spazi non sono inauditi (anche se la maggior parte dei tipi UNIX si acciglia su di loro). Detto questo, puoi semplificare senza preoccuparti ancora più facilmente degli spazi bianchi: find . -name '*.eml.gz' -exec zgrep "STRING" {} +questo ottiene gli stessi argomenti per il lancio di xargs, la sicurezza di -print0/ -0, e tutto senza l'overhead di un ulteriore processo di avvio e piping, e in modo abbastanza conciso. -execcon +è specificato POSIX, quindi dovrebbe essere sulla maggior parte dei sistemi semi-recenti UNIX simili a mia conoscenza.
ShadowRanger

@Jared C'è un modo per eseguire una ricerca jolly solo conoscendo l'inizio del modello di file? Ad esempio, ho file .gz che hanno timestamp di data / ora alla fine di essi. ABCLog04_18_18_2_21.gz C'è un modo per cercare ricorsivamente file che iniziano con ABC *. Ho provato a sostituire \*.eml.gznel tuo esempio sopra con ABCLog*un errore sul formato del file .: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

C'è molta confusione qui perché non ce n'è solo una zgrep. Ho due versioni sul mio sistema, zgrepda gzipe zgrepverso zutils. Il primo è solo uno script wrapper che chiama gzip -cdfq. Non supporta lo -r, --recursiveswitch. 1
Quest'ultimo è un c++programma e supporta l' -r, --recursiveopzione.
L'esecuzione zgrep --version | head -n 1rivelerà quale (se presente) è l'impostazione predefinita:

zgrep (gzip) 1.6

è lo script wrapper,

zgrep (zutils) 1.3

è l' cppeseguibile.
Se si dispone di quest'ultimo è possibile eseguire:

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

Comunque, come suggerito, find+ zgrepfunzionerà ugualmente bene con entrambe le versioni di zgrep:

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

Se zgrepmanca dal tuo sistema (altamente improbabile) puoi provare con:

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

ma c'è un grande svantaggio: non saprai dove si trovano le partite in quanto non esiste un nome file anteposto alle linee corrispondenti.


1: perché sarebbe problematico


1
se zgrepda zutils non è disponibile è possibile installarlo in Ubuntu con sudo apt-get install zutils.
therealmarv,

1
Continua da @therealmarv ... e poi Ubuntu userà zutils zgrep invece di gzip. Quindi -r funziona!
Elia Lynn

C'è un modo per stampare il numero di riga del file a cui corrisponde il motivo?
DogEatDog

@DogEatDog - proprio come grep -n, zgrep -nstamperà la riga n. È nel manuale ...
don_crissti

7

agè una variante di grep, con alcune belle funzionalità extra.

  • ha l'opzione -z per i file compressi,
  • ha molte funzioni ack.
  • è veloce

Così:

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

Se non installato,

apt-get install silversearcher-ag   (debian and friends)
yum install the_silver_searcher     (fedora)
brew install the_silver_searcher    (mac)

1
Di ag: truncated file: Successconseguenza ottengo . Qualsiasi altra bandiera dovrei aggiungere?
Anno

4

La ricorsione da sola è semplice:

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

Tuttavia, per i file compressi hai bisogno di qualcosa come:

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

path/to/directory dovrebbe essere la directory principale che contiene le sottodirectory per ogni giorno.


zgrepè la risposta ovvia ma, sfortunatamente, non supporta la -rbandiera. Da man zgrep:

Queste opzioni grep faranno terminare zgrep con un codice di errore: (- - [d rR zZ] | --di * | --exc * | --inc * | --rec * | --nu *).


3

Se il tuo sistema ha zgrep, puoi semplicemente

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

Se il tuo sistema non ha zgrep, puoi usare il comando find per eseguire zcat e grep su ogni file in questo modo:

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


Perdonatemi verde su questo ... i file da cercare sono profondi un paio di strati. ~ / gmvault-db / db / 2015-02 contiene una cartella per ogni mese archiviato, quindi sotto i quali vengono memorizzati i file .gz per quel mese. Se sto cercando .mil in tutto l'albero, è quello che farei? find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Ricerca {}"; zcat "{}" | grep .mil '\;
Kendor

1
Va bene - la "r" in -irs farà sì che zgrep cerchi in modo ricorsivo. Il comando find funziona in modo ricorsivo per impostazione predefinita, quindi qualsiasi file che termina in .gz verrà zcattato e passato in grep. (e {} verrà espanso nel percorso relativo del file che sta per essere cercato). Quindi, quando si ottiene un colpo, sarà preceduto da Searching ~/gmvault-db/db/2015-02/03/whatever.gz
Nate da Kalamazoo il

Ecco cosa torno indietro: trova: "i percorsi devono precedere l'espressione: -exec" Ecco il comando che ho usato: find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Ricerca { } "; zcat "{}" | grep .mil '\;
Kendor

eliminare la barra rovesciata tra '* .gz' e -exec.
Nate di Kalamazoo il

4
zgrepnon prenderà la -rbandiera per qualche motivo. Questo è menzionato in man zgrep(vedi anche la mia risposta).
terdon

0

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

xzgrep è un derivato dei programmi di utilità zgrep (less / bin / xzgrep)

Dalla pagina Man:

xzgrep invoca grep (1) su file che possono essere non compressi o compressi con xz (1), lzma (1), gzip (1), bzip2 (1) o lzop (1). Tutte le opzioni specificate vengono passate direttamente a grep (1).

-l stampa il nome del file corrispondente

-R per la ricorsione non funzionerà in quanto è specificamente vietato nella sceneggiatura, tuttavia il semplice globbing della shell dovrebbe farci arrivare lì

./*/*.eml.gz

da un percorso relativo in cui ./today/sample.eml.gz, corrispondono su tutte le istanze di un livello inferiore alla nostra posizione relativa nella shell, che termina con ".eml.gz"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.