¿Cómo grep recursivamente a través de archivos .gz?

135

Estoy usando un script para descargar regularmente mis mensajes de gmail que comprimen el .eml sin formato en archivos .gz. El script crea una carpeta para cada día y luego comprime cada mensaje en su propio archivo.

Me gustaría una forma de buscar en este archivo una "cadena".

Grep solo no parece hacerlo. También probé SearchMonkey.

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

Respuestas:

142

Si desea grep recursivamente en todos los archivos .eml.gz en el directorio actual, puede usar:

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

Tienes que escapar del primero *para que el shell no lo interprete. -print0le dice a find que imprima un carácter nulo después de cada archivo que encuentre; xargs -0lee de la entrada estándar y ejecuta el comando después de cada archivo; zgrepfunciona como grep, pero descomprime el archivo primero.

JK Stafford
fuente
3
'-print0' y '-0' no son obligatorios. xargs usa '\ n' por defecto.
Jaime M.
1
Son necesarios si puede haber caracteres de espacio en los caminos; No hay otra razón que la complejidad para no usarlos.
Daniel Griscom
2
zgrepen realidad parece más rápido que grepejecutarse en archivos sin comprimir. Debe ser porque los archivos comprimidos se pueden leer de la HD y descomprimir más rápido que leer un archivo sin comprimir de la HD.
Geremia
@JaimeM. xargsusa espacios en blanco (espacios en blanco) de forma predeterminada. Claro, los archivos casi nunca tienen líneas nuevas, pero los espacios no son desconocidos (incluso si la mayoría de los tipos UNIXy fruncen el ceño). Dicho esto, puede simplificar sin preocuparse por el espacio en blanco aún más fácilmente: find . -name '*.eml.gz' -exec zgrep "STRING" {} +obtiene los mismos muchos argumentos por lanzamiento xargs, la seguridad de -print0/ -0, y todo sin la sobrecarga de un proceso adicional de lanzamiento y tubería, y de manera bastante concisa. -execcon +se especifica POSIX, por lo que debería estar en la mayoría de los sistemas semi-recientes similares a UNIX, que yo sepa.
ShadowRanger
@Jared ¿Hay alguna forma de hacer una búsqueda con comodines solo conociendo el comienzo del patrón de archivo? Por ejemplo, tengo archivos .gz que tienen marcas de fecha / hora al final de ellos. ABCLog04_18_18_2_21.gz ¿Hay alguna manera de buscar de forma recursiva los archivos que comienzan con ABC *. Intenté reemplazar \*.eml.gzen su ejemplo anterior con ABCLog*y recibí un error sobre el formato de archivo .: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

Aquí hay mucha confusión porque no hay una sola zgrep. Tengo dos versiones en mi sistema, zgrepdesde gzipy zgrepdesde zutils. El primero es solo un script envoltorio que llama gzip -cdfq. No es compatible con el -r, --recursiveinterruptor. 1
Este último es un c++programa y admite la -r, --recursiveopción.
La ejecución zgrep --version | head -n 1revelará cuál de ellos (si corresponde) es el predeterminado:

zgrep (gzip) 1.6

es el script de envoltura,

zgrep (zutils) 1.3

es el cppejecutable
Si tienes este último, puedes ejecutar:

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

De todos modos, como se sugiere, find+ zgrepfuncionará igualmente bien con cualquier versión de zgrep:

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

Si zgrepfalta en su sistema (muy poco probable), puede probar con:

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

pero hay una desventaja importante: no sabrá dónde están las coincidencias ya que no hay un nombre de archivo antepuesto a las líneas coincidentes.


1: porque sería problemático

don_crissti
fuente
1
si zgrepdesde zutils no está disponible, puede instalarlo en Ubuntu con sudo apt-get install zutils.
therealmarv
1
Continúa desde @therealmarv ... y luego Ubuntu usará zutils zgrep en lugar de gzip. Entonces -r funciona!
Elijah Lynn
¿Hay alguna forma de imprimir el número de línea del archivo con el que coincide el patrón?
DogEatDog
@DogEatDog: al igual que grep -n, zgrep -nimprimirá los números de línea. Está en el manual ...
don_crissti
7

ages una variante de grep, con algunas características adicionales agradables.

  • tiene la opción -z para archivos comprimidos,
  • tiene muchas características ack.
  • es rápido

Entonces:

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

Si no está instalado,

apt-get install silversearcher-ag   (debian and friends)
yum install the_silver_searcher     (fedora)
brew install the_silver_searcher    (mac)
JJoao
fuente
1
Me sale ag: truncated file: Successcomo resultado. ¿Alguna otra bandera debo agregar?
Yar
4

La recursión sola es fácil:

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

Sin embargo, para archivos comprimidos necesita algo como:

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

path/to/directory debe ser el directorio principal que contiene los subdirectorios para cada día.


zgrepes la respuesta obvia pero, desafortunadamente, no es compatible con la -rbandera. De man zgrep:

Estas opciones grep harán que zgrep termine con un código de error: (- [d rR zZ] | --di * | --exc * | --inc * | --rec * | --nu *).

terdon
fuente
3

Si su sistema tiene zgrep, simplemente puede

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

Si su sistema no tiene zgrep, puede usar el comando find para ejecutar zcat y grep en cada archivo de la siguiente manera:

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

Nate de Kalamazoo
fuente
Perdóname por esto ... los archivos a buscar son un par de capas de profundidad. ~ / gmvault-db / db / 2015-02 contiene una carpeta para cada mes archivado, y luego debajo se almacenan los archivos .gz para ese mes. Si busco .mil dentro de todo ese árbol, ¿es eso lo que haría? encuentre ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Buscando {}"; zcat "{}" | grep .mil '\;
Kendor
1
Eso está bien: la "r" en -irs hará que zgrep busque de forma recursiva. El comando find funciona de forma recursiva de manera predeterminada, por lo que cualquier archivo que termine en .gz será zcat y se pasará a grep. (y {} se expandirá a la ruta relativa del archivo que está a punto de buscarse). Entonces, cuando recibas un golpe, será precedido por Searching ~/gmvault-db/db/2015-02/03/whatever.gz
Nate de Kalamazoo el
Esto es lo que obtengo: find: "las rutas deben preceder a la expresión: -exec" Aquí está el comando que utilicé: find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Buscando { } "; zcat "{}" | grep .mil '\;
Kendor
retire la barra invertida entre '* .gz' y -exec.
Nate de Kalamazoo
44
zgrepno tomará la -rbandera por alguna razón. Esa es la mención en man zgrep(también vea mi respuesta).
terdon
0

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

xzgrep es una derivada de las utilidades de zgrep (less / bin / xzgrep)

Desde la página Man:

xzgrep invoca grep (1) en archivos que pueden estar sin comprimir o comprimidos con xz (1), lzma (1), gzip (1), bzip2 (1) o lzop (1). Todas las opciones especificadas se pasan directamente a grep (1).

-l imprimo el nombre del archivo coincidente

-R para la recursión no funcionará, ya que está específicamente prohibido en el script, sin embargo, el simple bloqueo de shell debería llevarnos allí

./*/*.eml.gz

desde una ruta relativa donde ./today/sample.eml.gz, coincida en todas las instancias que estén un nivel por debajo de nuestra posición relativa en el shell, que termina con ".eml.gz"

John
fuente