Como recomprimir 2 milhões de arquivos gzip sem armazená-los duas vezes?

8

Eu tenho cerca de 2 milhões (60GiB) de arquivos pequenos compactados em gzip e gostaria de criar um arquivo compactado contendo todos eles em uma versão descompactada. Infelizmente, não posso simplesmente descompactá-los todos e criar o arquivo compactado, pois tenho apenas 70GiB de espaço livre em disco. Em outras palavras, como posso fazer um equivalente tar --file-filter="zcat" zcf file.tar.gz directoryse a opção de linha de comando --file-filternão existe no tar GNU?

d33tah
fonte
Você tem uma máquina multiprocessadora?
Anthon
1
@ Anthon: não nesta máquina, mas para os futuros leitores, podemos assumir que sim.
amigos estão dizendo sobre d33tah
Como você precisa recomprimir, há algo de ganho lá. Alguma razão específica para usar gzip? A combinação e a compactação economizam espaço, mas você ganharia muito mais se comprimir em um xzarquivo tar ed. Isso é uma opção?
Anthon
Qualquer programa de compactação faria. Se eu posso criar um arquivo tar dos arquivos descompactados, mas não armazenados, posso canalizá-lo para qualquer outro programa.
precisa

Respostas:

6

Uma opção poderia ser usar avfs(aqui assumindo um 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
Stéphane Chazelas
fonte
3

Observe que isso é frágil quando se trata de nomes de arquivos desagradáveis.

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"

Os arquivos são descompactados temporariamente em $tmpdir, transferidos para cpioentão assim que são adicionados ao arquivo, removidos.

Cristian Ciupitu
fonte
1
Além disso, se você tem múltiplas threads, sugiro usar pigzcomo uma alternativa para gzip :)
Christopher Stanley
2

Aqui está o que eu tentei até agora - parece funcionar, mas é terrivelmente lento, mesmo com o 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

d33tah
fonte
A descompressão e, especialmente, a recompressão em um disco que está quase cheio, será lento, não importa o quê.
Cristian Ciupitu
@CristianCiupitu: Eu medi sem |gzipe o arquivo não compactado não tocou basicamente no disco rígido, então IMHO não deve ser tão lento.
precisa
1
De e Recompressing são feitas no código C otimizado no CPython. Pode haver buffer envolvido que faz com que o disco não seja tocado.
Anthon
1
encontrar . -exec cat \ {\} \; > / dev / null deve fornecer um limite inferior para a quantidade de tempo que essa operação pode levar. Eu imagino que parte do seu problema é a criação de um monte de objetos python grandes que contêm seus arquivos em formas compactadas e descompactadas e, em seguida, deixar o coletor de lixo limpar você mesmo. veja aqui: stackoverflow.com/questions/6115066/…
BitShifter
Você provavelmente poderia economizar memória, descobrindo o tamanho descompactado e passando para taro arquivo gzip como objeto.
Cristian Ciupitu