Encontre recursivamente todos os arquivos de diversos formatos de arquivo e procure por padrões de nome de arquivo

11

Na melhor das hipóteses, gostaria de receber uma ligação como esta:

$searchtool /path/to/search/ -contained-file-name "*vacation*jpg"

... para que esta ferramenta

  • faz uma varredura recursiva do caminho especificado
  • pega todos os arquivos com formatos de arquivo suportados, que devem ser pelo menos os "mais comuns", como zip, rar, 7z, tar.bz, tar.gz ...
  • e verifique a lista de arquivos do arquivo morto para o padrão de nome em questão (aqui *vacation*jpg)

Estou ciente de como usar a ferramenta de localização, tar, descompactar e similares. Eu poderia combiná-los com um script de shell, mas estou procurando uma solução simples que possa ser uma linha de shell ou uma ferramenta dedicada (dicas para ferramentas da GUI são bem-vindas, mas minha solução deve ser baseada em linha de comando).

mdo
fonte

Respostas:

9

(Adaptado de Como eu grep recursivamente através de arquivos compactados? )

Instale o AVFS , um sistema de arquivos que fornece acesso transparente dentro dos arquivos. Primeiro, execute este comando uma vez para configurar uma visão do sistema de arquivos da sua máquina no qual você pode acessar os arquivos como se fossem diretórios:

mountavfs

Depois disso, se /path/to/archive.zipfor um arquivo reconhecido, ~/.avfs/path/to/archive.zip#é um diretório que parece conter o conteúdo do arquivo.

find ~/.avfs"$PWD" \( -name '*.7z' -o -name '*.zip' -o -name '*.tar.gz' -o -name '*.tgz' \) \
     -exec sh -c '
                  find "$0#" -name "*vacation*.jpg"
                 ' {} 'Test::Version' \;

Explicações:

  • Monte o sistema de arquivos AVFS.
  • Procure por arquivos ~/.avfs$PWDcompactados, que é a visualização AVFS do diretório atual.
  • Para cada arquivo morto, execute o snippet de shell especificado (com $0= nome do arquivo e $1= padrão para pesquisar).
  • $0#é a visualização do diretório do arquivo morto $0.
  • {\}em vez de {}é necessária no caso dos exteriores findsubstitutos {}dentro -exec ;argumentos (alguns o fazem, outros não).

Ou em zsh ≥4.3:

mountavfs
ls -l ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip)(e\''
     reply=($REPLY\#/**/*vacation*.jpg(.N))
'\')

Explicações:

  • ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip) corresponde aos arquivos na visualização 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. Definir a replymatriz transforma a correspondência em uma lista de nomes.
  • $REPLY\# é a visualização do diretório do arquivo morto.
  • $REPLY\#/**/*vacation*.jpgcorresponde aos *vacation*.jpgarquivos no arquivo morto.
  • O Nqualificador global faz com que o padrão se expanda para uma lista vazia, se não houver correspondência.
Gilles 'SO- parar de ser mau'
fonte
9

Se você quiser algo mais simples que a solução AVFS, escrevi um script Python para fazer isso chamado arkfind . Você pode realmente fazer

$ arkfind /path/to/search/ -g "*vacation*jpg"

Isso é feito recursivamente, para que você possa ver os arquivos dentro dos arquivos a uma profundidade arbitrária.

detly
fonte
Obrigado, boa contribuição! Especialmente se o AVFS não for uma opção.
mdo 5/07/2013
Seria ótimo se ele suporta arquivos jar.
9114 Chemik
@Chemik - observou ! Vou trabalhar um pouco mais neste final de semana :) O JAR não deve ser muito difícil, acredito que seja apenas um arquivo zip para o mundo exterior.
detly
@ Chemik - eu apenas tentei, e ele deve suportar arquivos JAR em sua forma atual de qualquer maneira. Você pode testá-lo e, se não funcionar como o esperado, arquivar um bug na página do Github? (Eu fiz apenas corrigir um bug, por isso certifique-se de atualizar sua cópia.)
detly
1
Sim, eu vejo agora, funciona. Você pode adicionar "arquivos JAR" ao README :)
Chemik
2

Minha solução usual :

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|DESIRED_FILE_TO_SEARCH'

Exemplo:

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|characterize.txt'

Os resultados são como:

foozip1.zip:
foozip2.zip:
foozip3.zip:
    DESIRED_FILE_TO_SEARCH
foozip4.zip:
...

Se você deseja apenas o arquivo zip com hits :

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|FILENAME' | grep -B1 'FILENAME'

O nome do arquivo aqui é usado duas vezes, para que você possa usar uma variável.

Com o find, você pode usar PATH / TO / SEARCH

Rodrigo Gurgel
fonte
2

Outra solução que funciona é zgrep

zgrep -r filename *.zip
John Oxley
fonte
1
Que implementação zgrepé essa? Isso não funciona com aquele fornecido com o GNU gzip( /bin/zgrep: -r: option not supported, zgrep (gzip) 1.6)
Stéphane Chazelas
2

A facilidade de uso do IMHO deve ser algo importante também:

 while read -r zip_file ; do echo "$zip_file" ; unzip -l "$zip_file" | \
 grep -i --color=always -R "$to_srch"; \
 done < <(find . \( -name '*.7z' -o -name '*.zip' \)) | \
 less -R

e para alcatrão (este não foi testado ...)

 while read -r tar_file ; do echo "$tar_file" ; tar -tf  "$tar_file" | \
 grep -i --color=always -R "$to_srch"; \
 done < <(find . \( -name '*.tar.gz' -o -name '*.tar' \)) | \
 less -R
Yordan Georgiev
fonte
Que unzipimplementação pode lidar com arquivos 7z ou tar.gz?
Stéphane Chazelas 23/09/16
Sim, isso é um bug ... corrigido ... Definitivamente, devemos usar os binários corretos para os tipos de arquivos corretos ... Eu apenas pretendi demonstrar o one-liner .. jee este quase chegará ao estado de estar pronto como how-to-recep ...
Yordan Georgiev
0

libarchive's bsdtarpodem lidar com a maioria desses formatos de arquivo, assim que você poderia fazer:

find . \( -name '*.zip' -o     \
          -name '*.tar' -o     \
          -name '*.tar.gz' -o  \
          -name '*.tar.bz2' -o \
          -name '*.tar.xz' -o  \
          -name '*.tgz' -o     \
          -name '*.tbz2' -o    \
          -name '*.7z' -o      \
          -name '*.iso' -o     \
          -name '*.cpio' -o    \
          -name '*.a' -o       \
          -name '*.ar' \)      \
       -type f                 \
       -exec bsdtar tf {} '*vacation*jpg' \; 2> /dev/null

Com o que você pode simplificar (e melhorar para corresponder a maiúsculas e minúsculas) com o GNU find:

find . -regextype egrep \
       -iregex '.*\.(zip|7z|iso|cpio|ar?|tar(|\.[gx]z|\.bz2)|tgz|tbz2)' \
       -type f \
       -exec bsdtar tf {} '*vacation*jpg' \; 2> /dev/null

Isso não imprime o caminho do arquivo onde esses *vacation*jpgarquivos são encontrados. Para imprimir esse nome, você pode substituir a última linha por:

-exec sh -ac '
   for ARCHIVE do
     bsdtar tf "$ARCHIVE" "*vacation*jpg" |
       awk '\''{print ENVIRON["ARCHIVE"] ": " $0}'\''
   done' sh {} + 2> /dev/null

que fornece uma saída como:

./a.zip: foo/blah_vacation.jpg
./a.zip: bar/blih_vacation.jpg
./a.tar.gz: foo/blah_vacation.jpg
./a.tar.gz: bar/blih_vacation.jpg

Ou com zsh:

setopt extendedglob # best in ~/.zshrc
for archive (**/*.(#i)(zip|7z|iso|cpio|a|ar|tar(|.gz|.xz|.bz2)|tgz|tbz2)(.ND)) {
  matches=("${(f@)$(bsdtar tf $archive '*vacation*jpg' 2> /dev/null)"})
  (($#matches)) && printf '%s\n' "$archive: "$^matches
}

Note-se que há uma série de outros formatos de arquivo que são apenas zipou tgzarquivos disfarçados como .jarou .docxarquivos. Você pode adicioná-los ao seu padrão find/ zshpesquisa, bsdtarnão se importa com a extensão (como em, ele não depende da extensão para determinar o tipo do arquivo).

Observe que *vacation*.jpgacima é correspondido no caminho completo do membro do arquivamento, não apenas no nome do arquivo; portanto, ele corresponderá, vacation.jpgmas também ativará vacation/2014/file.jpg.

Para corresponder apenas ao nome do arquivo, um truque seria usar o modo de extração , use -s(substituição) que usa regexps com um psinalizador para imprimir os nomes dos arquivos correspondentes e, em seguida, verifique se nenhum arquivo foi extraído, como:

bsdtar -'s|.*vacation[^/]*$||' -'s|.*||' -xf "$archive"

Note que ele produziria a lista no stderr e acrescentaria >>a cada linha. De qualquer forma ,, bsdtarcomo a maioria das tarimplementações, os nomes dos arquivos são mostrados se eles contiverem caracteres como nova linha ou barra invertida (renderizada como \nou \\).

Stéphane Chazelas
fonte