Come ricomprimere 2 milioni di file gzip senza memorizzarli due volte?


8

Ho circa 2 milioni (60GiB) di piccoli file compressi con gzip e vorrei creare un archivio compresso contenente tutti in una versione non compressa. Sfortunatamente, non posso semplicemente decomprimerli tutti e quindi creare l'archivio compresso in quanto ho solo circa 70GiB di spazio libero su disco. In altre parole, come posso fare un equivalente di tar --file-filter="zcat" zcf file.tar.gz directoryse l'opzione della riga di comando come --file-filternon esiste in tar GNU?


Hai una macchina multiprocessore?
Anthon,

1
@Anthon: non su questa macchina, ma per i futuri lettori potremmo supporre che sì.
d33tah,

Come devi ricomprimere, c'è qualcosa che guadagna lì. Qualche motivo particolare per cui usarlo gzip? Combinando e comprimendo si risparmia spazio, ma si otterrebbe molto di più se si comprimesse in un xzfile tar con -ed. È un'opzione?
Anthon,

Qualsiasi programma di compressione farebbe. Se riesco a creare un file tar dei file decompressi ma non memorizzati, posso inviarlo a qualsiasi altro programma.
d33tah,

Risposte:


6

Un'opzione potrebbe essere quella di utilizzare avfs(assumendo qui un sistema GNU):

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

3

Nota che questo è fragile quando si tratta di nomi di file cattivi.

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"

I file vengono temporaneamente decompressi sotto $tmpdir, passati a cpionon appena aggiunti all'archivio, rimossi.


1
Inoltre, se hai multi-thread, suggerirei di utilizzare pigzcome alternativa a gzip :)
Christopher Stanley,

2

Ecco cosa ho provato finora: sembra funzionare, ma è terribilmente lento, anche con PyPy:

#!/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()

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


Non comprimere e soprattutto ricomprimere su un disco che è quasi pieno, sarà lento, non importa quale.
Cristian Ciupitu,

@CristianCiupitu: ho misurato senza |gzipe il file non compresso non ha sostanzialmente toccato l'HDD, quindi IMHO non dovrebbe essere così lento.
d33tah,

1
De e ricompressione viene eseguita in codice C ottimizzato in CPython. Potrebbe esserci un buffering che potrebbe non toccare il disco.
Anthon,

1
trova . -exec cat \ {\} \; > / dev / null dovrebbe fornire un limite inferiore alla quantità di tempo che questa operazione potrebbe richiedere. Immagino che parte del tuo problema sia la creazione di un mucchio di grandi oggetti Python contenenti i tuoi file in entrambe le forme compresse e non compresse e quindi lasciare che il garbage collector si ripulisca. vedere qui: stackoverflow.com/questions/6115066/...
BitShifter

Probabilmente potresti risparmiare un po 'di memoria capendo le dimensioni non compresse e passando al tarfile gzip come oggetto.
Cristian Ciupitu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.