Wie greife ich rekursiv durch komprimierte Archive?

16

Ich versuche herauszufinden, welche Module use Test::Versionin cpan sind. Also habe ich es immer minicpangespiegelt. Mein Problem ist, dass ich die heruntergeladenen Archive durchlaufen und die Dateien in den Archiven überprüfen muss. Kann mir jemand sagen, wie ich das machen könnte? Am besten auf eine Weise, die mir sagt, in welcher Datei sich das Archiv befindet und in welcher Zeile es sich befindet.

(Hinweis: Es sind nicht alle Tarballs, einige sind Zip-Dateien)

Xenoterracid
quelle

Antworten:

18

Ok, wenden wir die Unix-Philosophie an. Was sind die Komponenten dieser Aufgabe?

  • Textsuche: Sie benötigen ein Tool zum Suchen von Text in einer Datei, z grep.
  • Rekursiv: Sie benötigen ein Tool, um in einem Verzeichnisbaum nach Dateien zu suchen, z find.
  • Archive: Sie benötigen ein Tool, um sie zu lesen.

Die meisten Unix-Programme arbeiten mit Dateien. Damit Sie Archivkomponenten problemlos bearbeiten können, müssen Sie auf sie als Dateien zugreifen, dh, Sie müssen auf sie als Verzeichnisse zugreifen.

Das AVFS- Dateisystem zeigt eine Ansicht des Dateisystems an, in dem auf jede Archivdatei /path/to/foo.zipals Verzeichnis zugegriffen werden kann~/.avfs/path/to/foo/zip# . AVFS bietet schreibgeschützten Zugriff auf die gängigsten Archivdateiformate.

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

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= Suchmuster).
  • $0#ist die Verzeichnisansicht des Archivs $0.
  • {\}eher als {}die äußeren , falls erforderlich findErsatz {}innerhalb -exec ;Argumente (einige tun es, manche nicht).
  • Optional: Hängen Sie das AVFS-Dateisystem endgültig aus.

Oder in zsh ≥4.3:

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

Erklärungen:

  • ~/.avfs$PWD/**/*.(tgz|tar.gz|zip) Stimmt mit Archiven in der AVFS-Ansicht des aktuellen Verzeichnisses und seiner Unterverzeichnisse überein.
  • PATTERN(e\''CODE'\')Wendet CODE auf jedes Match von PATTERN an. Der Name der übereinstimmenden Datei ist in $REPLY. Durch Festlegen des replyArrays wird die Übereinstimmung in eine Liste von Namen umgewandelt.
  • $REPLY\# ist die Verzeichnisansicht des Archivs.
  • $REPLY\#/**/*.pmStimmt mit .pmDateien im Archiv überein .
  • Das NGlob-Qualifikationsmerkmal erweitert das Muster zu einer leeren Liste, wenn keine Übereinstimmung vorliegt.
Gilles 'SO - hör auf böse zu sein'
quelle
Dies schafft das andere interessante Problem, dass alle Archive gemountet und dann wieder gemountet werden müssen. Ein Teil des Problems ist, dass 22.000 Archive durchsucht werden müssen
Xenoterracide
@xenoterracide: Wie ist das ein Problem? Mit AVFS haben Sie einen einzelnen Mount-Punkt ( ~/.avfs) und der Zugriff auf jedes Archiv erfolgt automatisch ( ~/.avfs/path/to/archive.zip\#ist ein normales Verzeichnis im AVFS-Dateisystem, kein Mount-Punkt). Natürlich bedeutet jedes Archiv, auf das Sie zugreifen, einen kleinen Leistungseinbruch, aber das ist das eigentliche Problem.
Gilles 'SO- hör auf böse zu sein'
@gilles nur die Tatsache, dass ich jetzt durchgehen und herausfinden muss, wie man sie zuerst montiert, was ein bisschen eine schlechte Idee ist, sie besser zu montieren, wenn ich gehe und nach dem Durchsuchen aussteige.
Xenoterracide
@xenoterracide: Nochmals: Nein, Sie müssen sie nicht einzeln mounten. Der vollständige Workflow (abgesehen von der Installation von AVFS, falls erforderlich) befindet sich in meinen Codefragmenten.
Gilles 'SO- hör auf böse zu sein'
@gilles gut, ich muss mich ein bisschen damit auseinandersetzen ... weil ich find: missing argument to -exec'` bekomme und viele davon von zshzsh: Input/output error: Data-Maker-0.27
xenoterracide
0

Es scheint, dass ich es so machen kann

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

Dies führt jedoch zu folgenden Ergebnissen:

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

das ist nicht sehr spezifisch für wo im tarball. Hoffentlich kann jemand eine bessere Antwort finden.

Xenoterracid
quelle
0

Vielen Dank für die Herausforderung, die ich mir ausgedacht habe:

#!/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
Kyle Smith
quelle
Ich habe gerade Ihre Zeilennummernanforderung gesehen. Das kann wahrscheinlich mit einer Kombination aus grep -n und awk funktionieren, um die Zeilennummer zu erfassen. Das Auflisten des Dateinamens kann nicht so einfach sein wie grep -H, da es immer stdin ist und daher möglicherweise mehr Zeilen benötigt.
Kyle Smith
Fehler raus, wenn auf meinem System ausgeführt, unendlich 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
oft
Ich wusste auch nicht, als ich dies zum ersten Mal veröffentlichte, dass einige Archive auf cpan ZIP-Dateien sind.
Xenoterracide
Hm, ich habe mit einer Struktur von nur .tar.gz-Dateien getestet - es könnte robuster gemacht werden, um geeignete Maßnahmen basierend auf dem Dateityp zu ergreifen, aber dies sollte einen anständigen Ausgangspunkt geben.
Kyle Smith
0

Vielleicht hilft meine Antwort jemandem:

#!/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
Serge Roussak
quelle
0

Nach der Installation können p7zip-*Sie dies tun:

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

Sie müssen nicht lsvor der ersten Pipe verwenden, unabhängig von der Liste, in der die komprimierten Dateien funktionieren. Das Finale zeigt lessnur den Pfad des Listenlebens im komprimierten Archiv, nicht jedoch den Namen.

Roberto Robert
quelle
0

Verwenden Sie find, um alle erforderlichen Dateien zu finden, und das zgrep, um komprimierte Dateien zu untersuchen:

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

Hab das aber nicht auf Tarballs getestet

Iggy Pop
quelle