Encuentre recursivamente todos los archivos de diversos formatos de archivo y búsquelos por patrones de nombre de archivo

11

En el mejor de los casos, me gustaría recibir una llamada como esta:

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

... para que esta herramienta

  • hace una exploración recursiva de la ruta dada
  • toma todos los archivos con formatos de archivo compatibles que al menos deberían ser los "más comunes" como zip, rar, 7z, tar.bz, tar.gz ...
  • y escanee la lista de archivos del archivo en busca del patrón de nombre en cuestión (aquí *vacation*jpg)

Soy consciente de cómo usar la herramienta de búsqueda, tar, descomprimir y similares. Podría combinarlos con un script de shell, pero estoy buscando una solución simple que podría ser un one-liner de shell o una herramienta dedicada (las sugerencias para las herramientas GUI son bienvenidas, pero mi solución debe estar basada en la línea de comandos).

mdo
fuente

Respuestas:

9

(Adaptado de ¿Cómo busco recursivamente archivos comprimidos? )

Instale AVFS , un sistema de archivos que proporciona acceso transparente dentro de los archivos. Primero ejecute este comando una vez para configurar una vista del sistema de archivos de su máquina en la que pueda acceder a los archivos como si fueran directorios:

mountavfs

Después de esto, si /path/to/archive.zipes un archivo reconocido, entonces ~/.avfs/path/to/archive.zip#es un directorio que parece contener el contenido del archivo.

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' \;

Explicaciones:

  • Montar el sistema de archivos AVFS.
  • Busque archivos en ~/.avfs$PWD, que es la vista AVFS del directorio actual.
  • Para cada archivo, ejecute el fragmento de shell especificado (con $0= nombre de archivo y $1= patrón para buscar).
  • $0#es la vista de directorio del archivo $0.
  • {\}en lugar de {}ser necesario en caso de que los findsustitutos externos sean argumentos {}internos -exec ;(algunos lo hacen, otros no).

O en zsh ≥4.3:

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

Explicaciones:

  • ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip) coincide con los archivos en la vista AVFS del directorio actual y sus subdirectorios.
  • PATTERN(e\''CODE'\')aplica el CÓDIGO a cada coincidencia de PATRÓN. El nombre del archivo coincidente está en $REPLY. Establecer la replymatriz convierte la coincidencia en una lista de nombres.
  • $REPLY\# es la vista de directorio del archivo.
  • $REPLY\#/**/*vacation*.jpgcoincide con *vacation*.jpgarchivos en el archivo.
  • El Ncalificador global hace que el patrón se expanda a una lista vacía si no hay coincidencia.
Gilles 'SO- deja de ser malvado'
fuente
9

Si quieres algo más simple que la solución AVFS, escribí un script de Python para hacerlo llamado arkfind . En realidad solo puedes hacer

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

Lo hará de forma recursiva, por lo que puede mirar los archivos dentro de los archivos a una profundidad arbitraria.

desviarse
fuente
Gracias, buena contribución! Especialmente si AVFS no es una opción.
mdo
Sería genial si admite archivos jar.
Chemik
@Chemik - notado ! Haré un poco más de trabajo este fin de semana :) JAR no debería ser demasiado difícil, creo que en realidad es solo un archivo zip para el mundo exterior.
detly
@Chemik: acabo de probarlo y, de todos modos, debería admitir archivos JAR en su forma actual. ¿Puedes probarlo y, si no funciona como esperas, presentar un error en la página de Github? (Acabo de corregir un error, así que asegúrese de actualizar su copia.)
detly
1
Sí, ya veo, funciona. Puede agregar "archivos JAR" a README :)
Chemik
2

Mi solución habitual :

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

Ejemplo:

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

Los resultados son como:

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

Si solo desea el archivo zip con hits :

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

FILENAME aquí se usa dos veces, por lo que puede usar una variable.

Con find puedes usar PATH / TO / SEARCH

Rodrigo Gurgel
fuente
2

Otra solución que funciona es zgrep

zgrep -r filename *.zip
John Oxley
fuente
1
¿Qué implementación zgrepes esa? Eso no funciona con el enviado con GNU gzip( /bin/zgrep: -r: option not supported, zgrep (gzip) 1.6)
Stéphane Chazelas
2

En mi humilde opinión, la facilidad de uso también debería ser algo importante en bash:

 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

y para alquitrán (este no ha sido probado ...)

 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
fuente
¿Qué unzipimplementación puede manejar archivos 7z o tar.gz?
Stéphane Chazelas
sí, eso es un error ... corregido ... uno definitivamente debería usar los archivos binarios correctos para los tipos de archivo correctos ... Solo pretendía demostrar el one-liner ... jee, este casi llegará al estado de estar listo como recibo de instrucciones ...
Yordan Georgiev
0

libarchive's bsdtarpuede manejar la mayoría de esos formatos de archivo, por lo que podría hacer:

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

Que puede simplificar (y mejorar para que coincida entre mayúsculas y minúsculas) con GNU findcon:

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

Sin embargo, eso no imprime la ruta del archivo donde *vacation*jpgse encuentran esos archivos. Para imprimir ese nombre, puede reemplazar la última línea con:

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

que da una salida 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

O con 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
}

Tenga en cuenta que hay varios otros formatos de archivo que son justos zipo tgzarchivos disfrazados como .jaro .docxarchivos. Puede agregarlos a su patrón find/ zshsearch, bsdtarno le importa la extensión (como en, no depende de la extensión para determinar el tipo de archivo).

Tenga en cuenta que lo *vacation*.jpganterior coincide con la ruta completa del miembro de archivo, no solo con el nombre del archivo, por lo que coincidiría con, vacation.jpgpero también con vacation/2014/file.jpg.

Para hacer coincidir solo el nombre del archivo, un truco sería usar el modo de extracción , usar -s(sustitución) que usa expresiones regulares con una pbandera para imprimir los nombres de los archivos coincidentes y luego asegurarse de que no se extraiga ningún archivo, como:

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

Tenga en cuenta que generará la lista en stderr y se agregará >>a cada línea. En cualquier caso, bsdtarcomo la mayoría de las tarimplementaciones, pueden alterar los nombres de los archivos que se muestran si contienen algunos caracteres como nueva línea o barra diagonal inversa (representados como \no \\).

Stéphane Chazelas
fuente