Comment puis-je récursivement grep à travers des archives compressées?


16

J'essaie de savoir quels modules use Test::Versiondans cpan. J'ai donc l'habitude minicpande le refléter. Mon problème est que je dois parcourir les archives téléchargées et grep les fichiers qui se trouvent dans les archives. Quelqu'un peut-il me dire comment je pourrais faire cela? de préférence d'une manière qui me dit quel fichier dans l'archive et sur quelle ligne il se trouve.

(Remarque: ce ne sont pas toutes des tarballs, certains sont des fichiers zip)

Réponses:


18

Ok, appliquons la philosophie Unix. Quelles sont les composantes de cette tâche?

  • Recherche de texte: vous avez besoin d'un outil pour rechercher du texte dans un fichier, tel que grep .
  • Récursif: vous avez besoin d'un outil pour rechercher des fichiers dans une arborescence de répertoires, comme find.
  • Archives: vous avez besoin d'un outil pour les lire.

La plupart des programmes Unix fonctionnent sur des fichiers. Donc, pour fonctionner facilement sur des composants d'archives, vous devez y accéder en tant que fichiers, en d'autres termes, vous devez y accéder en tant que répertoires.

Le système de fichiers AVFS présente une vue du système de fichiers où chaque fichier d'archive /path/to/foo.zipest accessible en tant que répertoire ~/.avfs/path/to/foo/zip#. AVFS fournit un accès en lecture seule aux formats de fichiers d'archives les plus courants.

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

Explications:

  • Montez le système de fichiers AVFS.
  • Recherchez les fichiers d'archive dans ~/.avfs$PWD, qui est la vue AVFS du répertoire en cours.
  • Pour chaque archive, exécutez l'extrait de shell spécifié (avec $0= nom de l'archive et $1= modèle à rechercher).
  • $0#est la vue du répertoire de l'archive $0.
  • {\}plutôt que {}nécessaire dans le cas où les findsubstituts externes à l' {}intérieur des -exec ;arguments (certains le font, d'autres pas).
  • Facultatif: démontez enfin le système de fichiers AVFS.

Ou en zsh ≥4.3:

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

Explications:

  • ~/.avfs$PWD/**/*.(tgz|tar.gz|zip) correspond aux archives dans la vue AVFS du répertoire courant et de ses sous-répertoires.
  • PATTERN(e\''CODE'\')applique le CODE à chaque match de MOTIF. Le nom du fichier correspondant est dans $REPLY. La définition du replytableau transforme la correspondance en une liste de noms.
  • $REPLY\# est la vue du répertoire de l'archive.
  • $REPLY\#/**/*.pmcorrespond aux .pmfichiers de l'archive.
  • Le Nqualificatif glob transforme le modèle en liste vide s'il n'y a pas de correspondance.

cela crée l'autre problème intéressant de devoir monter puis démonter toutes les archives, car le problème est qu'il y a 22k archives qui doivent être recherchées
xenoterracide

@xenoterracide: En quoi est-ce un problème? Avec AVFS, vous avez un seul point de montage ( ~/.avfs), et l'accès à chaque archive est automatique ( ~/.avfs/path/to/archive.zip\#c'est un répertoire ordinaire sur le système de fichiers AVFS, pas un point de montage). Bien sûr, chaque archive à laquelle vous accédez signifie un peu de performances, mais c'est intrinsèque au problème.
Gilles 'SO- arrête d'être méchant'

@gilles seulement le fait que maintenant je dois passer au travers et comprendre comment les monter d'abord, ce qui semble un peu une mauvaise idée, mieux vaut les monter au fur et à mesure et démonter après avoir été fouillé.
xénoterracide

@xenoterracide: Encore une fois: non, vous n'avez pas besoin de les monter individuellement. Le flux de travail complet (à l'exception de l'installation d'AVFS si nécessaire) se trouve dans mes extraits de code.
Gilles 'SO- arrête d'être méchant'

@gilles bien je vais devoir creuser un peu ... parce que je reçois find: missing argument to -exec'` et beaucoup de ceci de zshzsh: Input/output error: Data-Maker-0.27
xenoterracide

0

Il semble que je puisse le faire de cette façon

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

Cependant, cela donne des résultats comme:

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

ce qui n'est pas très spécifique à l'endroit où dans l'archive. J'espère que quelqu'un pourra trouver une meilleure réponse.


0

Merci pour le défi, j'ai trouvé:

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

Je viens de voir votre exigence de numéro de ligne. Cela peut probablement fonctionner avec une combinaison de grep -n et awk pour capturer le numéro de ligne. Ne peut pas être aussi simple que grep -H pour lister le nom de fichier car il est toujours stdin, donc peut nécessiter plus de lignes.
Kyle Smith

erreurs 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
survenues

aussi, je ne savais pas quand j'ai posté pour la première fois que certaines des archives sur cpan étaient des fichiers zip.
xenoterracide

Hm, j'ai testé avec une structure de fichiers .tar.gz uniquement - il pourrait être rendu plus robuste pour prendre des mesures appropriées en fonction du type de fichier, mais cela devrait donner un point de départ décent.
Kyle Smith

0

Peut-être que ma réponse sera utile pour quelqu'un:

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

Après l'installation, p7zip-*vous pouvez faire ceci:

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

Vous n'avez pas besoin d'utiliser lsavant le premier canal, quelle que soit la liste des fichiers compressés fonctionnera. La finale lessne montrera que le CHEMIN de la vie du listet à l'intérieur de l'archive compressée, mais pas le nom de celui-ci.


0

Utilisez find pour localiser tous les fichiers nécessaires et zgrep pour rechercher les fichiers compressés:

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

Je n'ai pas testé cela sur des tarballs

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.