¿Cómo volver a comprimir 2 millones de archivos gzip sin almacenarlos dos veces?

8

Tengo alrededor de 2 millones (60GiB) de archivos pequeños comprimidos y me gustaría crear un archivo comprimido que contenga todos ellos en una versión sin comprimir. Desafortunadamente, no puedo descomprimirlos todos y luego crear el archivo comprimido ya que solo tengo alrededor de 70GiB de espacio libre en disco. En otras palabras, ¿cómo puedo hacer un equivalente de tar --file-filter="zcat" zcf file.tar.gz directorysi el cambio de línea de comando como --file-filterno existe en GNU tar?

d33tah
fuente
¿Tienes una máquina multiprocesador?
Anthon
1
@Anthon: no en esta máquina, pero para los futuros lectores podríamos asumir que sí.
d33tah
Como tienes que volver a comprimir, hay algo que ganar allí. ¿Alguna razón particular por qué usar gzip? Combinar y comprimir ahorra espacio, pero ganaría mucho más si comprimiera en un xzarchivo tar editado. ¿Es esa una opción?
Anthon
Cualquier programa de compresión lo haría. Si puedo crear un archivo tar de los archivos descomprimidos pero no almacenados, puedo canalizarlo a cualquier otro programa.
d33tah

Respuestas:

6

Una opción podría ser usar avfs(suponiendo aquí 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
Stéphane Chazelas
fuente
3

Tenga en cuenta que esto es frágil cuando se trata de nombres de archivos desagradables.

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"

Los archivos se descomprimen temporalmente debajo $tmpdir, se pasan a ellos cpiotan pronto como se agregan al archivo, se eliminan.

Cristian Ciupitu
fuente
1
Además, si tiene varios subprocesos, sugeriría usar pigzcomo alternativa a gzip :)
Christopher Stanley
2

Esto es lo que probé hasta ahora: parece funcionar, pero es terriblemente lento, incluso 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

d33tah
fuente
Descomprimir y especialmente recomprimir en un disco que está casi lleno, será lento, pase lo que pase.
Cristian Ciupitu
@CristianCiupitu: Medí sin |gzipy el archivo sin comprimir básicamente no tocó el HDD, por lo que en mi humilde opinión no debería ser TAN lento.
d33tah
1
De y Recompressing se realiza en código C optimizado en CPython. Puede haber un búfer involucrado que haga que el disco no se toque.
Anthon
1
encontrar . -exec cat \ {\} \; > / dev / null debería proporcionar un límite inferior en la cantidad de tiempo que esta operación podría tomar. Me imagino que parte de su problema es la creación de un montón de objetos grandes de Python que contienen sus archivos en formas comprimidas y no comprimidas y luego dejar que el recolector de basura se limpie después de usted. ver aquí: stackoverflow.com/questions/6115066/…
BitShifter
Probablemente podría ahorrar algo de memoria averiguando el tamaño sin comprimir y pasando al tararchivo gzip como objeto.
Cristian Ciupitu