Wie kann ich 2 Millionen gzip-Dateien erneut komprimieren, ohne sie zweimal zu speichern?

8

Ich habe ungefähr 2 Millionen (60 GB) kleine komprimierte Dateien und möchte ein komprimiertes Archiv erstellen, das alle in einer unkomprimierten Version enthält. Leider kann ich nicht alle dekomprimieren und dann das komprimierte Archiv erstellen, da ich nur etwa 70 GB freien Speicherplatz habe. Mit anderen Worten, wie kann ich ein Äquivalent dazu erstellen, tar --file-filter="zcat" zcf file.tar.gz directorywenn der Befehlszeilenschalter wie --file-filterin GNU tar nicht vorhanden ist?

d33tah
quelle
Haben Sie eine Multiprozessor-Maschine?
Anthon
1
@Anthon: Nicht auf dieser Maschine, aber für die zukünftigen Leser könnten wir das ja annehmen.
d33tah
Wenn Sie erneut komprimieren müssen, gibt es dort etwas zu gewinnen. Gibt es einen bestimmten Grund, warum zu verwenden gzip? Das Kombinieren und Komprimieren spart Platz, aber Sie würden viel mehr gewinnen, wenn Sie in eine xz-ed-TAR-Datei komprimieren würden . Ist das eine Option?
Anthon
Jedes Komprimierungsprogramm würde ausreichen. Wenn ich eine TAR-Datei der dekomprimierten, aber nicht gespeicherten Dateien erstellen kann, kann ich sie an jedes andere Programm weiterleiten.
d33tah

Antworten:

6

Eine Option könnte sein avfs(hier unter der Annahme eines GNU-Systems):

mkdir ~/AVFS &&
avfsd ~/AVFS &&
cd ~/AVFS/where/your/gz/files/are/ &&
find . -name '*.gz' -type f -printf '%p#\0' |
  tar --null -T - --transform='s/.gz#$//' -cf - | pigz > /dest/file.tar.gz
Stéphane Chazelas
quelle
3

Beachten Sie, dass dies fragil ist, wenn es um böse Dateinamen geht.

dir_with_small_files=/home/john/files
tmpdir=/tmp/ul/dst
tarfile=/tmp/ul.tar
mkfifo "${tarfile}"

gzip <"${tarfile}" >"${tarfile}.gz" &

find "$dir_with_small_files" -type f | \
while read src; do
    dstdir="${tmpdir}/$(dirname $src)"
    dst="$(basename $src .gz)"
    mkdir -p "$dstdir"
    gunzip <"$src" >"${dstdir}/${dst}"
    # rm "$src" # uncomment to remove the original files
    echo "${dstdir}/${dst}"
done | \
cpio --create --format=ustar -v --quiet 2>&1 >"${tarfile}" | \
while read x; do
    rm "$x"
done

# clean-up
rm "$tarfile"
rm -r "$tmpdir"

Die Dateien werden vorübergehend unter dekomprimiert $tmpdirund an sie übergeben, cpiosobald sie dem Archiv hinzugefügt und entfernt wurden.

Cristian Ciupitu
quelle
1
Auch wenn Sie Multi-Threads haben, würde ich vorschlagen, pigzals Alternative zu gzip zu verwenden :)
Christopher Stanley
2

Folgendes habe ich bisher versucht - es scheint zu funktionieren, ist aber selbst mit PyPy furchtbar langsam:

#!/usr/bin/python

import tarfile
import os
import gzip
import sys
import cStringIO

tar = tarfile.open("/dev/stdout", "w|")
for name in sys.stdin:
    name = name[:-1]  # remove the trailing newline
    try:
        f = gzip.open(name)
        b = f.read()
        f.close()
    except IOError:
        f = open(name)
        b = f.read()
        f.close()
    # the [2:] there is to remove ./ from "find" output
    ti = tarfile.TarInfo(name[2:])
    ti.size = len(b)
    io = cStringIO.StringIO(b)
    tar.addfile(ti, io)
tar.close()

Verwendungszweck: find . | script.py | gzip > file.tar.gz

d33tah
quelle
Das Dekomprimieren und insbesondere das erneute Komprimieren auf einer fast vollen Festplatte wird langsam sein, egal was passiert.
Cristian Ciupitu
@CristianCiupitu: Ich habe ohne gemessen |gzipund die unkomprimierte Datei hat die Festplatte im Grunde nicht berührt, also sollte es meiner Meinung nach nicht so langsam sein.
d33tah
1
Das De- und Rekomprimieren erfolgt in optimiertem C-Code in CPython. Möglicherweise liegt eine Pufferung vor, die dazu führt, dass die Disc nicht berührt wird.
Anthon
1
finden . -exec cat \ {\} \; > / dev / null sollte eine Untergrenze für die Zeit angeben, die diese Operation möglicherweise dauern könnte. Ich würde mir vorstellen, dass ein Teil Ihres Problems darin besteht, eine Reihe großer Python-Objekte zu erstellen, die Ihre Dateien sowohl in komprimierter als auch in unkomprimierter Form enthalten, und dann den Garbage Collector nach sich selbst bereinigen zu lassen. siehe hier: stackoverflow.com/questions/6115066/…
BitShifter
Sie könnten wahrscheinlich etwas Speicherplatz sparen, indem Sie die unkomprimierte Größe herausfinden und tarwie ein Objekt an die gzip-Datei übergeben.
Cristian Ciupitu