Recherchez récursivement tous les fichiers d'archive de divers formats d'archive et recherchez-les pour les modèles de nom de fichier

11

Au mieux, j'aimerais avoir un appel comme celui-ci:

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

... pour que cet outil

  • fait un scan récursif du chemin donné
  • prend tous les fichiers avec des formats d'archives pris en charge qui devraient au moins être les "plus courants" comme zip, rar, 7z, tar.bz, tar.gz ...
  • et scannez la liste des fichiers de l'archive pour le modèle de nom en question (ici *vacation*jpg)

Je sais comment utiliser l'outil de recherche, tar, unzip et similaire. Je pourrais les combiner avec un script shell mais je cherche une solution simple qui pourrait être un shell one-liner ou un outil dédié (les astuces pour les outils GUI sont les bienvenues mais ma solution doit être basée sur la ligne de commande).

mdo
la source

Réponses:

9

(Adapté de Comment puis-je récursivement grep à travers des archives compressées? )

Installez AVFS , un système de fichiers qui fournit un accès transparent à l'intérieur des archives. Exécutez d'abord cette commande une fois pour configurer une vue du système de fichiers de votre machine dans laquelle vous pouvez accéder aux archives comme s'il s'agissait de répertoires:

mountavfs

Après cela, si /path/to/archive.zipest une archive reconnue, alors ~/.avfs/path/to/archive.zip#est un répertoire qui semble contenir le contenu de l'archive.

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

Explications:

  • Montez le système de fichiers AVFS.
  • Recherchez les fichiers d'archives dans ~/.avfs$PWD, qui est la vue AVFS du répertoire actuel.
  • 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).

Ou en zsh ≥4.3:

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

Explications:

  • ~/.avfs$PWD/**/*.(7z|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\#/**/*vacation*.jpgcorrespond aux *vacation*.jpgfichiers de l'archive.
  • Le Nqualificatif glob transforme le modèle en liste vide s'il n'y a pas de correspondance.
Gilles 'SO- arrête d'être méchant'
la source
9

Si vous voulez quelque chose de plus simple que la solution AVFS, j'ai écrit un script Python pour le faire appelé arkfind . Vous pouvez simplement faire

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

Il le fera récursivement, vous pouvez donc regarder les archives à l'intérieur des archives à une profondeur arbitraire.

detly
la source
Merci, belle contribution! Surtout si AVFS n'est pas une option.
mdo
Ce serait formidable s'il prend en charge les fichiers jar.
Chemik
@Chemik - noté ! Je vais y travailler un peu plus ce week-end :) JAR ne devrait pas être trop dur, je pense que c'est vraiment juste un fichier zip pour le monde extérieur.
detly
@Chemik - Je viens de l'essayer et il devrait de toute façon prendre en charge les fichiers JAR dans sa forme actuelle. Pouvez-vous le tester et, si cela ne fonctionne pas comme prévu, déposer un bogue sur la page Github? (Je viens de corriger un bug, alors assurez-vous de mettre à jour votre copie.)
Detly
1
Oui je vois maintenant, ça marche. Vous pouvez ajouter des "fichiers JAR" à README :)
Chemik
2

Ma solution habituelle :

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

Exemple:

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

Les résultats sont comme:

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

Si vous ne voulez que le fichier zip avec des hits dessus:

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

FILENAME ici est utilisé deux fois, vous pouvez donc utiliser une variable.

Avec find, vous pouvez utiliser PATH / TO / SEARCH

Rodrigo Gurgel
la source
2

Une autre solution qui fonctionne est zgrep

zgrep -r filename *.zip
John Oxley
la source
1
De quelle mise en œuvre zgreps'agit-il? Cela ne fonctionne pas avec celui livré avec GNU gzip( /bin/zgrep: -r: option not supported, zgrep (gzip) 1.6)
Stéphane Chazelas
2

La convivialité à mon humble avis devrait également être une chose 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

et pour le goudron (celui-ci n'a pas été testé ...)

 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
la source
Quelle unzipimplémentation peut gérer les fichiers 7z ou tar.gz?
Stéphane Chazelas
ouais c'est un bug ... corrigé ... il faut absolument utiliser les bons binaires pour les bons types de fichiers ... Je visais juste à démontrer le one-liner .. jee celui-ci va presque arriver à l'état prêt comme recette pratique ...
Yordan Georgiev
0

libarchive« s bsdtarpeut traiter la plupart de ces formats de fichiers, vous pouvez donc faire:

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 vous pouvez simplifier (et améliorer pour correspondre à la casse) avec GNU findavec:

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

Cela n'imprime pas le chemin de l'archive où *vacation*jpgse trouvent ces fichiers. Pour imprimer ce nom, vous pouvez remplacer la dernière ligne par:

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

ce qui donne une sortie comme:

./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 avec 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
}

Notez qu'il existe un certain nombre d'autres formats de fichiers qui sont juste zipou des tgzfichiers déguisés comme .jarou des .docxfichiers. Vous pouvez les ajouter à votre modèle de recherche find/ zsh, bsdtarne vous souciez pas de l'extension (comme dans, il ne dépend pas de l'extension pour déterminer le type du fichier).

Notez que ce qui *vacation*.jpgprécède est mis en correspondance sur le chemin d'accès complet aux membres de l'archive, pas seulement sur le nom du fichier, il correspondra donc sur vacation.jpgmais aussi sur vacation/2014/file.jpg.

Pour faire correspondre le nom de fichier uniquement, une astuce consiste à utiliser le mode d' extraction , à utiliser -s(substitution) qui utilise des expressions rationnelles avec un pindicateur pour imprimer les noms des fichiers correspondants, puis à vous assurer qu'aucun fichier n'est extrait, comme:

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

Notez qu'il afficherait la liste sur stderr et l'ajouterait >>à chaque ligne. Dans tous les cas, bsdtarcomme la plupart des tarimplémentations, les noms de fichiers affichés peuvent être modifiés s'ils contiennent des caractères tels que la nouvelle ligne ou la barre oblique inverse (rendue sous la forme \nou \\).

Stéphane Chazelas
la source