Как повторно сжать 2 миллиона файлов gzip, не сохраняя их дважды?

8

У меня есть около 2 миллионов (60 ГБ) сжатых небольших файлов, и я хотел бы создать сжатый архив, содержащий все из них в несжатой версии. К сожалению, я не могу просто распаковать их все, а затем создать сжатый архив, так как у меня всего около 70 ГБ свободного дискового пространства. Другими словами, как я могу сделать эквивалент, tar --file-filter="zcat" zcf file.tar.gz directoryесли параметр командной строки, как, например --file-filter, не существует в GNU tar?

d33tah
источник
У вас есть многопроцессорная машина?
Антон
1
@Anthon: не на этой машине, но для будущих читателей мы можем предположить, что да.
d33tah
Поскольку вы должны перепрессовать, там есть что-то выигрышное. Любая конкретная причина, почему использовать gzip? Комбинирование и сжатие экономит место, но вы получите гораздо больше, если xzсжимаете в файл tar с -ed. Это вариант?
Антон
Подойдет любая программа сжатия. Если я могу создать tar-файл из распакованных, но не сохраненных файлов, я могу передать его любой другой программе.
d33tah

Ответы:

6

Можно использовать вариант avfs(здесь предполагается, что система 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

Обратите внимание, что это хрупкое, когда дело доходит до грязных имен файлов.

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"

Файлы временно распаковываются $tmpdir, cpioзатем передаются, как только они добавляются в архив, удаляются.

Кристиан Чиупиту
источник
1
Кроме того, если у вас есть многопоточность, я бы предложил использовать pigzв качестве альтернативы gzip :)
Кристофер Стэнли,
2

Вот что я попробовал до сих пор - похоже, работает, но очень медленно, даже с 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()

Применение: find . | script.py | gzip > file.tar.gz

d33tah
источник
Распаковка и особенно повторное сжатие на диске, который почти заполнен, будет медленным, несмотря ни на что.
Кристиан Чиупиту
@CristianCiupitu: Я измерял без, |gzipи несжатый файл не касался жесткого диска, поэтому, ИМХО, он не должен быть ТАКИМ медленным.
d33tah
1
Де и Рекомпрессия выполняется в оптимизированном C-коде в CPython. Это может быть связано с буферизацией, из-за которой диск не трогать.
Антон
1
найти . -exec cat \ {\} \; > / dev / null должен обеспечивать нижнюю границу количества времени, которое может занять эта операция. Я полагаю, что частью вашей проблемы является создание группы больших объектов Python, содержащих ваши файлы как в сжатой, так и в несжатой форме, и затем сборщик мусора убирает за собой. смотрите здесь: stackoverflow.com/questions/6115066/…
BitShifter
Вероятно, вы могли бы сэкономить немного памяти, вычислив несжатый размер и передав в tarфайл gzip объект.
Кристиан Чупиту