¿Cómo puedo buscar recursivamente archivos comprimidos?


16

Estoy tratando de averiguar qué módulos use Test::Versionen cpan. Así que solía minicpanreflejarlo. Mi problema es que necesito iterar por los archivos que se descargan y seleccionar los archivos que están en los archivos. ¿Alguien puede decirme cómo puedo hacer esto? preferiblemente de una manera que me dice qué archivo del archivo y en qué línea está.

(nota: no todos son tarballs, algunos son archivos zip)

Respuestas:


18

Ok, apliquemos la filosofía de Unix. ¿Cuáles son los componentes de esta tarea?

  • Búsqueda de texto: necesita una herramienta para buscar texto en un archivo, como grep.
  • Recursivo: necesita una herramienta para buscar archivos en un árbol de directorios, como find.
  • Archivos: necesitas una herramienta para leerlos.

La mayoría de los programas de Unix funcionan con archivos. Por lo tanto, para operar fácilmente en componentes de archivo, debe acceder a ellos como archivos, en otras palabras, debe acceder a ellos como directorios.

El sistema de archivos AVFS presenta una vista del sistema de archivos donde /path/to/foo.zipse puede acceder a cada archivo como un directorio ~/.avfs/path/to/foo/zip#. AVFS proporciona acceso de solo lectura a los formatos de archivo de almacenamiento más comunes.

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

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).
  • Opcional: finalmente desmonte el sistema de archivos AVFS.

O en zsh ≥4.3:

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

Explicaciones:

  • ~/.avfs$PWD/**/*.(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\#/**/*.pmcoincide con .pmarchivos en el archivo.
  • El Ncalificador global hace que el patrón se expanda a una lista vacía si no hay coincidencia.

esto crea el otro problema de tener que montar y luego desmontar todos los archivos, ya que parte del problema es que hay 22k archivos que deben buscarse
xenoterracida

@xenoterracide: ¿Cómo es eso un problema? Con AVFS, tiene un único punto de montaje ( ~/.avfs), y el acceso a cada archivo es automático ( ~/.avfs/path/to/archive.zip\#es un directorio ordinario en el sistema de archivos AVFS, no un punto de montaje). Claro, cada archivo al que accede significa un pequeño impacto en el rendimiento, pero eso es intrínseco al problema.
Gilles 'SO- deja de ser malvado'

@gilles solo el hecho de que ahora tengo que pasar y descubrir cómo montarlos primero, lo que parece una mala idea, mejor montarlos a medida que avanzo y desmontar después de haber sido buscado.
xenoterracide

@xenoterracide: Nuevamente: no, no necesitas montarlos individualmente. El flujo de trabajo completo (aparte de instalar AVFS si es necesario) está en mis fragmentos de código.
Gilles 'SO- deja de ser malvado'

@gilles bueno, tendré que profundizar un poco en esto ... porque obtengo find: missing argument to -exec '' y mucho de esto de zshzsh: Input/output error: Data-Maker-0.27
xenoterracide

0

Parece que puedo hacerlo de esta manera

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

Sin embargo, esto da resultados como:

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

que no es muy específico de dónde en el tarball. Esperemos que alguien pueda encontrar una mejor respuesta.


0

Gracias por el desafío, se me ocurrió:

#!/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

Acabo de ver el requisito de número de línea. Eso probablemente puede funcionar con alguna combinación de grep -n y awk para capturar el número de línea. No puede ser tan simple como grep -H para enumerar el nombre de archivo ya que siempre es estándar, por lo que puede requerir más líneas.
Kyle Smith

errores cuando se ejecuta en mi sistema, infinito repetido:tar (child): conform.tar.gz: Cannot open: No such file or directory tar (child): Error is not recoverable: exiting now tar: Child returned status 2 tar: Error is not recoverable: exiting now
xenoterracide

tampoco me di cuenta cuando publiqué esto por primera vez que algunos de los archivos en cpan son archivos zip.
xenoterracida

Hm, lo probé con una estructura de solo archivos .tar.gz: podría hacerse más robusto para tomar las medidas adecuadas según el tipo de archivo, pero esto debería dar un punto de partida decente.
Kyle Smith

0

Tal vez mi respuesta sea útil para alguien:

#!/bin/bash

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

# 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

0

Después de instalar p7zip-*puedes hacer esto:

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

No tiene que usar lsantes de la primera tubería, cualquiera que sea la lista que funcionará con los archivos comprimidos. La final lesssolo mostrará la RUTA de la vida de escucha dentro del archivo comprimido, pero no el nombre de esta.


0

Use find para localizar todos los archivos necesarios, y ese zgrep para buscar en archivos comprimidos:

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

Aunque no probé esto en tarballs

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.