Como faço para recursivamente grep através de arquivos compactados?

14

Estou tentando descobrir quais módulos use Test::Version no cpan. Então usei minicpan para espelhá-lo. Meu problema é que eu preciso percorrer os arquivos baixados e os arquivos que estão nos arquivos. Alguém pode me dizer como eu poderia fazer isso? de preferência de uma forma que me diga qual arquivo no arquivo e em que linha está.

(nota: eles não são todos os arquivos do tar, alguns são arquivos zip)

    
por xenoterracide 25.05.2011 / 14:29

6 respostas

15

Ok, vamos aplicar a filosofia unix. Quais são os componentes desta tarefa?

  • Pesquisa de texto: você precisa de uma ferramenta para pesquisar texto em um arquivo, como grep .
  • Recursivo: você precisa de uma ferramenta para procurar arquivos em uma árvore de diretórios, como find .
  • Arquivos: você precisa de uma ferramenta para lê-los.

A maioria dos programas unix operam em arquivos. Portanto, para operar facilmente em componentes de arquivo, você precisa acessá-los como arquivos, em outras palavras, você precisa acessá-los como diretórios.

O sistema de arquivos AVFS apresenta uma visão do sistema de arquivos em que cada arquivo /path/to/foo.zip está acessível como um diretório ~/.avfs/path/to/foo/zip# . O AVFS fornece acesso somente leitura aos formatos de arquivo mais comuns.

mountavfs
find ~/.avfs"$PWD" \( -name '*.zip' -o -name '*.tar.gz' -o -name '*.tgz' \) \
     -exec sh -c '
                  find "$0#" -name "*.pm" -exec grep "$1" {\} +
                 ' {} 'Test::Version' \;
fusermount -u ~/.avfs   # optional

Explicações:

  • Monte o sistema de arquivos AVFS.
  • Procure por arquivos archive em ~/.avfs$PWD , que é a visualização do AVFS do diretório atual.
  • Para cada arquivo, execute o fragmento de shell especificado (com $0 = nome do arquivo e $1 = padrão a ser pesquisado).
  • $0# é a visualização de diretório do arquivo $0 .
  • {\} em vez de {} é necessário no caso dos argumentos externos find substituírem {} dentro de -exec ; (alguns fazem isso, outros não).
  • Opcional: finalmente, desmonte o sistema de arquivos AVFS.

Ou em zsh ≥4.3:

mountavfs
grep 'Test::Version' ~/.avfs$PWD/**/*.(tgz|tar.gz|zip)(e\''
     reply=($REPLY\#/**/*.pm(.N))
'\')

Explicações:

  • ~/.avfs$PWD/**/*.(tgz|tar.gz|zip) corresponde a arquivos na visualização do AVFS do diretório atual e de seus subdiretórios.
  • PATTERN(e\''CODE'\') aplica CODE a cada correspondência de PATTERN. O nome do arquivo correspondente está em $REPLY . A configuração da matriz reply transforma a correspondência em uma lista de nomes.
  • $REPLY\# é a visualização de diretório do arquivo.
  • $REPLY\#/**/*.pm corresponde a .pm ficheiros no arquivo.
  • O qualificador N glob faz com que o padrão seja expandido para uma lista vazia se não houver correspondência.
por 26.05.2011 / 00:35
0

Parece que posso fazer assim

find authors/ -type f -exec zgrep "Test::Version" '{}' +  

No entanto, isso gera resultados como:

authors/id/J/JO/JONASBN/Module-Info-File-0.11.tar.gz:Binary file (standard input) matches

que não é muito específico para onde no tarball. Espero que alguém possa encontrar uma resposta melhor.

    
por 25.05.2011 / 15:37
0

Obrigado pelo desafio, eu criei:

#!/bin/bash
#

# tarballs to check in
find authors/ -type f | while read tarball; do

    # get list of files in tarball (not dirs ending in /):
    tar tzf $tarball | grep -v '/$' | while read file; do       

        # get contents of file and look for string
        tar -Ozxf conform.tar.gz $file | grep -q 'Text::Version' && echo "Tar ($tarball) has matching File ($file)"

    done

done
    
por 25.05.2011 / 16:22
0

Talvez minha resposta seja útil para alguém:

#!/bin/bash

findpath=$(echo $1 | sed -r 's|(.*[^/]$)|/|')

# tarballs to check in
find $findpath -type f | while read tarball; do

    # get list of files in tarball (not dirs ending in /):
    if [ -n "$(file --mime-type $tarball | grep -e "application/jar")" ]; then

        jar tf $tarball | grep -v '/$' | while read file; do
            # get contents of file and look for string
            grepout=$(unzip -q -c $tarball $file | grep $3 -e "$2")

            if [ -n "$grepout" ]; then
                echo "*** $tarball has matching file ($file):"
                echo $grepout
            fi

        done

    elif tar -tf $tarball 2>/dev/null; then

        tar -tf $tarball | grep -v '/$' | while read file; do
            # get contents of file and look for string
            grepout=$(unzip -q -c $tarball $file | grep $3 -e "$2")

            if [ -n "$grepout" ]; then
                echo "*** $tarball has matching file ($file):"
                echo $grepout
            fi

        done

    else
        file=""
        grepout=$(grep $3 -e "$2" $tarball)

        if [ -n "$grepout" ]; then
            echo "*** $tarball has matching:"
            echo $grepout
        fi

    fi

done
    
por 25.04.2013 / 15:33
0

Depois de instalar o p7zip-* , você pode fazer isso:

ls | xargs -I {} 7z l {} | grep whatever | less

Você não precisa usar ls antes do primeiro canal, seja qual for a lista em que os arquivos compactados funcionem. O% final less apenas mostrará o PATH da vida do listet dentro do archive compactado, mas não o nome dele.

    
por 12.06.2014 / 01:12
0

Use a localização para localizar todos os arquivos necessários e o zgrep para examinar os arquivos compactados:

find <folder> -type f -name "<search criteria[*gz,*bz...]>" -execdir zgrep -in "<grep expression>" '{}' ';'

Não testei isso em tarballs embora

    
por 27.11.2015 / 13:36