Entfernen Sie Dateien effizient aus großen .tgz-Dateien

14

Angenommen, ich habe eine mit gzip komprimierte tar-ball compressArchive.tgz (+100 Dateien, insgesamt +5 GB).

Was wäre der schnellste Weg, um alle Einträge zu entfernen, die mit einem bestimmten Dateinamenmuster übereinstimmen, zum Beispiel mit dem Präfix * .jpg, und die Überreste dann wieder in einem gzip: ed tar-ball zu speichern?

Es ist nicht wichtig, das alte Archiv zu ersetzen oder ein neues zu erstellen, je nachdem, welches am schnellsten ist.

Aksel Willgert
quelle

Antworten:

14

Mit GNU tarkönnen Sie:

pigz -d < file.tgz |
  tar --delete --wildcards -f - '*/prefix*.jpg' |
  pigz > newfile.tgz

Mit bsdtar:

pigz -d < file.tgz |
  bsdtar -cf - --exclude='*/prefix*.jpg' @- |
  pigz > newfile.tgz

( pigzIst die Multithread-Version von gzip).

Sie können die Datei wie folgt überschreiben:

{ pigz -d < file.tgz |
    tar --delete --wildcards -f - '*/prefix*.jpg' |
    pigz &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file.tgz

Aber das ist ziemlich riskant, vor allem , wenn die Folge endet als weniger komprimiert als die Originaldatei (in diesem Fall, der zweite pigzkann das Überschreiben Bereich der Datei am Ende , die die erste noch nicht gelesen).

Stéphane Chazelas
quelle
danke für die antwort, upvoted. werde nächste Woche einen Benchmark durchführen, um zu sehen, welche für mein Archiv und mein System besser abschneidet, und das akzeptieren.
Aksel Willgert
8

Diskontieren Sie nicht den einfachen Weg: Es kann für Ihren Zweck schnell genug sein. Mit avfs können Sie als Verzeichnis auf das Archiv zugreifen:

cd ~/.avfs/path/to/original.tar.gz\#
pax -w -s '/^.*\.jpg$//' | gzip >/path/to/filtered.tar.gz        # POSIX
tar -czf /path/to/filtered.tar.gz -s '/^.*\.jpg$//' .            # BSD
tar -czf /path/to/filtered.tar.gz --transform '/^.*\.jpg$//' .   # GNU

Extrahieren Sie mit primitiveren Tools zuerst die Dateien mit Ausnahme der .jpgDateien und erstellen Sie dann ein neues Archiv.

mkdir tmpdir && cd tmpdir
<original.tar.gz gzip -d | pax -r -pe -s '/^.*\.jpg$//'
pax -w . | gzip >filtered.tar.gz
cd .. && rm -rf tmpdir

Wenn Ihr Teer hat --exclude:

mkdir tmpdir && cd tmpdir
tar -xzf original.tar.gz --exclude='*.jpg'
tar -czf filtered.tar.gz .
cd .. && rm -rf tmpdir

Dies kann jedoch den Besitz und die Modi von Dateien beeinträchtigen, wenn Sie sie nicht als Root ausführen. Verwenden Sie für optimale Ergebnisse ein temporäres Verzeichnis auf einem schnellen Dateisystem - tmpfs, wenn Sie eines haben, das groß genug ist.

Die Unterstützung für Archivierer, die als Passthrough fungieren (dh ein Archiv lesen und ein Archiv schreiben), ist in der Regel begrenzt. GNU tar kann mit der --deleteOption operation Mitglieder aus einem Archiv löschen ("Die --deleteOption hat sich als richtig erwiesen, wenn sie tarals Filter von stdinbis fungiert stdout."). Dies ist wahrscheinlich die beste Option für Sie.

In einigen Zeilen von Python können Sie leistungsstarke Archivfilter erstellen. Die tarfileBibliothek kann aus nicht suchbaren Streams lesen und schreiben, und Sie können beliebigen Code in Python verwenden, um zu filtern, umzubenennen, zu ändern ...

#!/usr/bin/python
import re, sys, tarfile
source = tarfile.open(fileobj=sys.stdin, mode='r|*')
dest = tarfile.open(fileobj=sys.stdout, mode='w|gz')
for member in source:
    if not (member.isreg() and re.match(r'.*\.jpg\Z', member.name)):
        sys.stderr.write(member.name + '\n')
        dest.addfile(member, source.extractfile(member))
dest.close()
Gilles 'SO - hör auf böse zu sein'
quelle
Es würde auch uid / usernames entstellen, wenn es als root ausgeführt wird, es sei denn, es wird auf einem Computer ausgeführt, der die gleiche uid <=> username-Zuordnung hat wie derjenige, auf dem die tar-Datei ursprünglich erstellt wurde. ACLs und erweiterte Attribute können ebenfalls betroffen sein. Mit tarmöchten Sie möglicherweise die pOption hinzufügen .
Stéphane Chazelas
2

Mit dem Teer, der unter Mac OS X verfügbar ist, können Sie Folgendes tun:

tar -czf b.tgz --exclude '*.jpg' @a.tgz
mv b.tgz a.tgz
Jake
quelle
1

Dazu müssen Sie wahrscheinlich den gesamten Inhalt der .tgz-Datei in ein lokales Verzeichnis extrahieren und dann die Dateien löschen, die Sie nicht möchten. Anschließend müssen Sie die .tgz-Datei erneut komprimieren.

Es ist lang und Sie benötigen ausreichend freien Speicherplatz, aber meines Wissens gibt es keine andere Möglichkeit, dies zu tun.

Vorausgesetzt , dass Sie bereits einige Pfad haben wie /tmpdir/withalotofspaceDASS sufficent freien Speicherplatz (überprüfen Sie es mit df -h /tmpdir/withalotofspace), können Sie etwas tun:

$ cd /tmpdir/withalotofspace
$ tar -xvfz /path/to/compressedArchive.tgz
$ find /tmpdir/withalotofspace/ -type f -iname '*.jpg' -delete
$ tar -cvzf /path/to/purgedcompressedArchive.tgz .
DavAlPi
quelle
Wie die anderen Antworten zeigen, müssen nicht komprimierte Daten zu keinem Zeitpunkt über Piping auf der Festplatte gespeichert werden
Tobias Kienzler,
0

Ich mag die Antwort von @Gilles, außer es kann weiter vereinfacht werden. Nach dem Entpacken wird zum Beispiel gunzip foo.tgzdie Datei foo.tarund Dateien können mit entfernt werden tar -f foo.tar --delete file|directory. Unten sehen Sie ein Beispiel für das Entfernen eines Verzeichnisses aus einer TAR-Datei.

    phablet@ubuntu-phablet:~/Downloads$ tar -cvf moo.tar moo1/
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -f moo.tar --delete "moo1/moo2/moo3"
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/

Bestimmte Dateitypen finden Sie mit tar -tf foo.tar|egrep -i '.jpg$'.

Funmungus
quelle