Suchen Sie rekursiv alle Archivdateien verschiedener Archivformate und suchen Sie sie nach Dateinamenmustern

11

Bestenfalls hätte ich gerne einen Anruf wie folgt:

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

... damit dieses Tool

  • führt einen rekursiven Scan des angegebenen Pfads durch
  • Nimmt alle Dateien mit unterstützten Archivformaten, die mindestens die "häufigsten" sein sollten, wie zip, rar, 7z, tar.bz, tar.gz ...
  • und scannen Sie die Dateiliste des Archivs nach dem fraglichen Namensmuster (hier *vacation*jpg)

Ich bin mir bewusst, wie man das Suchwerkzeug, tar, unzip und ähnliches verwendet. Ich könnte diese mit einem Shell-Skript kombinieren, suche aber nach einer einfachen Lösung, die ein Shell-Einzeiler oder ein dediziertes Tool sein kann (Hinweise zu GUI-Tools sind willkommen, aber meine Lösung muss befehlszeilenbasiert sein).

mdo
quelle

Antworten:

9

(Angepasst von Wie greife ich rekursiv durch komprimierte Archive? )

Installieren Sie AVFS , ein Dateisystem, das transparenten Zugriff auf Archive bietet. Führen Sie diesen Befehl zunächst einmal aus, um eine Ansicht des Dateisystems Ihres Computers einzurichten, in der Sie auf Archive zugreifen können, als wären sie Verzeichnisse:

mountavfs

Wenn /path/to/archive.zipes sich danach um ein erkanntes Archiv handelt, ~/.avfs/path/to/archive.zip#handelt es sich um ein Verzeichnis, das den Inhalt des Archivs zu enthalten scheint.

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

Erklärungen:

  • Hängen Sie das AVFS-Dateisystem ein.
  • Suchen Sie nach Archivdateien in ~/.avfs$PWDder AVFS-Ansicht des aktuellen Verzeichnisses.
  • Führen Sie für jedes Archiv das angegebene Shell-Snippet aus (mit $0= Archivname und $1= zu durchsuchendem Muster).
  • $0#ist die Verzeichnisansicht des Archivs $0.
  • {\}eher als {}die äußeren , falls erforderlich findErsatz {}innerhalb -exec ;Argumente (einige tun es, manche nicht).

Oder in zsh ≥4.3:

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

Erklärungen:

  • ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip) stimmt mit Archiven in der AVFS-Ansicht des aktuellen Verzeichnisses und seiner Unterverzeichnisse überein.
  • PATTERN(e\''CODE'\')wendet CODE auf jede Übereinstimmung von MUSTER an. Der Name der übereinstimmenden Datei ist in$REPLY . Durch das Festlegen des replyArrays wird die Übereinstimmung in eine Liste von Namen umgewandelt.
  • $REPLY\# ist die Verzeichnisansicht des Archivs.
  • $REPLY\#/**/*vacation*.jpg Streichhölzer *vacation*.jpg Dateien im Archiv .
  • Das NGlob-Qualifikationsmerkmal erweitert das Muster zu einer leeren Liste, wenn keine Übereinstimmung vorliegt.
Gilles 'SO - hör auf böse zu sein'
quelle
9

Wenn Sie etwas Einfacheres als die AVFS-Lösung wünschen, habe ich dafür ein Python-Skript namens arkfind geschrieben . Sie können eigentlich nur tun

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

Dies geschieht rekursiv, sodass Sie Archive in Archiven in einer beliebigen Tiefe betrachten können.

detly
quelle
Danke, schöner Beitrag! Besonders wenn AVFS keine Option ist.
mdo
Es wäre großartig, wenn es JAR-Dateien unterstützen würde.
Chemik
@Chemik - notiert ! Ich werde dieses Wochenende ein bisschen mehr daran arbeiten :) JAR sollte nicht zu schwer sein, ich glaube, es ist wirklich nur eine Zip-Datei für die Außenwelt.
Detly
@Chemik - Ich habe es gerade versucht und es sollte sowieso JAR-Dateien in seiner aktuellen Form unterstützen. Können Sie es testen und wenn es nicht wie erwartet funktioniert, einen Fehler auf der Github-Seite melden? (Ich habe gerade einen Fehler behoben, also stellen Sie sicher, dass Sie Ihre Kopie aktualisieren.)
Detly
1
Ja ich sehe jetzt, es funktioniert. Sie können "JAR-Dateien" zu README hinzufügen :)
Chemik
2

Meine übliche Lösung:

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

Beispiel:

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

Ergebnisse sind wie:

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

Wenn Sie nur die Zip-Datei mit Treffern möchten :

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

Der Dateiname wird hier zweimal verwendet, sodass Sie eine Variable verwenden können.

Mit find können Sie PATH / TO / SEARCH verwenden

Rodrigo Gurgel
quelle
2

Eine andere Lösung, die funktioniert, ist zgrep

zgrep -r filename *.zip
John Oxley
quelle
1
Was für eine Implementierung zgrepist das? Das funktioniert nicht mit dem mit GNU gzip( /bin/zgrep: -r: option not supported, zgrep (gzip) 1.6) ausgelieferten
Stéphane Chazelas
2

IMHO Benutzerfreundlichkeit sollte auch eine Sache in der Bash sein:

 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

und für Teer (dieser ist ungetestet ...)

 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
quelle
Welche unzipImplementierung kann mit 7z- oder tar.gz-Dateien umgehen?
Stéphane Chazelas
Ja, das ist ein Fehler ... behoben ... man sollte auf jeden Fall die richtigen Binärdateien für die richtigen Dateitypen verwenden ... Ich wollte nur den Einzeiler demonstrieren ... jee, dieser wird fast fertig sein als How-to-Quittung ...
Yordan Georgiev
0

libarchiveDie bsdtarmeisten dieser Dateiformate können verarbeitet werden. Sie können also Folgendes tun:

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

Was Sie mit GNU vereinfachen (und verbessern können, um die Groß- und Kleinschreibung nicht zu berücksichtigen) findmit:

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

Der Pfad des Archivs, in dem sich diese *vacation*jpgDateien befinden, wird jedoch nicht gedruckt . Um diesen Namen zu drucken, können Sie die letzte Zeile durch Folgendes ersetzen:

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

das gibt eine Ausgabe wie:

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

Oder mit 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
}

Beachten Sie, dass es eine Reihe anderer Dateiformate gibt, die nur zipoder tgzDateien in Verkleidung wie .jaroder .docxDateien sind. Sie können diese zu Ihrem find/ zshSuchmuster hinzufügen.bsdtar ohne sich um die Erweiterung zu kümmern (wie in, es hängt nicht von der Erweiterung ab, um den Dateityp zu bestimmen).

Beachten Sie, dass *vacation*.jpgoben auf dem vollständigen Pfad des Archivmitglieds nicht nur der Dateiname übereinstimmt, vacation.jpgsondern auch auf vacation/2014/file.jpg.

Um nur mit dem Dateinamen übereinzustimmen , besteht ein Trick darin, den Extraktionsmodus zu verwenden . Verwenden Sie -s(Ersetzung), bei dem reguläre Ausdrücke mit einem pFlag verwendet werden, um die Namen der übereinstimmenden Dateien zu drucken, und stellen Sie dann sicher, dass keine Datei extrahiert wird, wie z.

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

Beachten Sie, dass die Liste auf stderr ausgegeben und >>an jede Zeile angehängt wird. In jedem Fall können bsdtar, wie bei den meisten tarImplementierungen, die angezeigten Dateinamen beschädigt werden, wenn sie Zeichen wie Zeilenumbruch oder Backslash (gerendert als \noder \\) enthalten.

Stéphane Chazelas
quelle