Como grep recursivamente através de arquivos .gz?

135

Estou usando um script para baixar regularmente minhas mensagens do Gmail que compactam o arquivo .eml bruto em arquivos .gz. O script cria uma pasta para cada dia e, em seguida, compacta todas as mensagens em seu próprio arquivo.

Eu gostaria de uma maneira de pesquisar neste arquivo por uma "string".

Grep sozinho não parece fazê-lo. Eu também tentei o SearchMonkey.

Kendor
fonte
16
use zgrep:zgrep - search possibly compressed files for a regular expression
Arkadiusz Drabczyk

Respostas:

141

Se você deseja grep recursivamente em todos os arquivos .eml.gz no diretório atual, você pode usar:

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

Você precisa escapar do primeiro *para que o shell não o interprete. -print0diz ao find para imprimir um caractere nulo após cada arquivo encontrado; xargs -0lê da entrada padrão e executa o comando após cada arquivo; zgrepfunciona como grep, mas descompacta o arquivo primeiro.

JK Stafford
fonte
2
'-print0' e '-0' não são obrigatórios. O xargs usa '\ n' por padrão.
Jaime M.
1
Eles são necessários se houver caracteres de espaço nos caminhos; não há outro motivo além da complexidade para não usá-los.
Daniel Griscom 23/09
2
zgrepna verdade, parece mais rápido do que grepexecutado em arquivos não compactados. Deve ser porque os arquivos compactados podem ser lidos no HD e descompactados mais rapidamente do que ler um arquivo não compactado do HD.
Geremia
@JaimeM. xargsusa espaços em branco (espaço em branco) por padrão. Certamente, os arquivos quase nunca têm novas linhas, mas os espaços não são desconhecidos (mesmo que a maioria dos tipos UNIXy os desaproveite). Dito isso, você pode simplificar sem se preocupar com o espaço em branco de maneira ainda mais fácil: find . -name '*.eml.gz' -exec zgrep "STRING" {} +isso gera os mesmos argumentos por lançamento xargs, a segurança de -print0/ -0e tudo sem a sobrecarga de um processo e inicialização de processo extras e de maneira bastante concisa. -execcom o +POSIX especificado, portanto, ele deve estar na maioria dos sistemas semi-recentes do tipo UNIX, que eu saiba.
ShadowRanger
@ Jared Existe uma maneira de fazer uma pesquisa curinga apenas conhecendo o início do padrão de arquivo? Por exemplo, eu tenho arquivos .gz que possuem carimbos de data / hora no final deles. ABCLog04_18_18_2_21.gz Existe uma maneira de procurar recursivamente arquivos começando com ABC *. Eu tentei substituir \*.eml.gzno seu exemplo acima por ABCLog*e obter um erro sobre o formato do arquivo .: #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

Há muita confusão aqui, porque não há apenas uma zgrep. Eu tenho duas versões no meu sistema, zgrepde gzipe zgreppara zutils. O primeiro é apenas um script de invólucro que chama gzip -cdfq. Não suporta o -r, --recursiveswitch. 1
O último é um c++programa e suporta a -r, --recursiveopção.
A execução zgrep --version | head -n 1revelará qual deles (se houver) é o padrão:

zgrep (gzip) 1.6

é o script do wrapper,

zgrep (zutils) 1.3

é o cppexecutável.
Se você tiver o último, poderá executar:

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

De qualquer forma, como sugerido, o find+ zgrepfuncionará igualmente bem com qualquer uma das versões de zgrep:

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

Se zgrepestiver ausente do seu sistema (altamente improvável), você pode tentar:

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

mas há uma grande desvantagem: você não saberá onde estão as correspondências, pois não há um nome de arquivo anexado às linhas correspondentes.


1: porque seria problemático

don_crissti
fonte
1
Se o zgrepzutils não estiver disponível, você poderá instalá-lo no Ubuntu com sudo apt-get install zutils.
Therealmarv
1
Continua a partir de @therealmarv ... e o Ubuntu usa o zutils zgrep em vez do gzip. Então -r funciona!
Elias Lynn
Existe uma maneira de imprimir o número da linha do arquivo em que o padrão corresponde?
precisa saber é o seguinte
@DogEatDog - assim como grep -n, zgrep -nimprimirá a linha no.s. Ele está no manual ... #
1174 don_crissti
7

agé uma variante do grep, com alguns recursos extras agradáveis.

  • possui a opção -z para arquivos compactados,
  • tem muitos recursos de reconhecimento.
  • isso é rápido

Assim:

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

Se não estiver instalado,

apt-get install silversearcher-ag   (debian and friends)
yum install the_silver_searcher     (fedora)
brew install the_silver_searcher    (mac)
JJoao
fonte
1
Eu recebo ag: truncated file: Successcomo resultado. Qualquer outra bandeira devo adicionar?
11287 Yar
4

A recursão sozinha é 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.

No entanto, para arquivos compactados, você precisa de algo como:

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

path/to/directory deve ser o diretório pai que contém os subdiretórios para cada dia.


zgrepé a resposta óbvia, mas, infelizmente, não suporta a -rbandeira. De man zgrep:

Essas opções de grep farão com que o zgrep termine com um código de erro: (- [d rR zZ ] | --di * | --exc * | --inc * | --rec * | --nu *).

terdon
fonte
3

Se o seu sistema possui o zgrep, você pode simplesmente

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

Se o seu sistema não possui zgrep, você pode usar o comando find para executar o zcat e o grep em cada arquivo da seguinte maneira:

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

Nate de Kalamazoo
fonte
Perdoe-me a veracidade disso ... os arquivos a serem pesquisados ​​têm algumas camadas de profundidade. ~ / gmvault-db / db / 2015-02 contém uma pasta para cada mês arquivado e, em seguida, abaixo dos arquivos .gz desse mês, são armazenados. Se eu procuro .mil dentro dessa árvore inteira, é isso que eu faria? encontre ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Pesquisando {}"; zcat "{}" | grep .mil '\;
Kendor
1
Tudo bem - o "r" em -irs fará com que o zgrep procure recursivamente. O comando find opera recursivamente por padrão, portanto, qualquer arquivo que termina em .gz será zcatted e passado para grep. (e o {} será expandido para o caminho relativo do arquivo que está prestes a ser pesquisado). Então, quando você receber um acerto, ele será precedido por Searching ~/gmvault-db/db/2015-02/03/whatever.gz
Nate de Kalamazoo
Aqui está o que eu volto: find: "caminhos devem preceder a expressão: -exec" Aqui está o comando que eu usei: find ~ / gmvault-db / db / -name '* .gz' \ -exec sh -c 'echo "Pesquisando { } "; zcat "{}" | grep .mil '\;
Kendor
retire a barra invertida entre o '* .gz' e o -exec.
Nate de Kalamazoo
4
zgrepnão levará a -rbandeira por algum motivo. Isso é mencionado man zgrep(veja também minha resposta).
terdon
0

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

xzgrep é um derivado dos utilitários do zgrep (menos / bin / xzgrep)

Na página Man:

O xzgrep chama grep (1) em arquivos que podem ser descompactados ou compactados com xz (1), lzma (1), gzip (1), bzip2 (1) ou lzop (1). Todas as opções especificadas são passadas diretamente para grep (1).

-l imprime o nome do arquivo correspondente

-R para recursão não funcionará, pois é especificamente proibido no script, no entanto, simples globbing de shell deve nos levar até lá

./*/*.eml.gz

a partir de um caminho relativo em que ./today/sample.eml.gz, corresponda em todas as instâncias desse nível um abaixo da nossa posição relativa no shell, que termina com ".eml.gz"

John
fonte