Как tar.gz много файлов одинакового размера в несколько архивов с ограничением размера

11

Я на Ubuntu 16.04.

У меня есть папка с большим количеством текстовых файлов (почти 12k). Мне нужно загрузить их все на веб-сайт, который принимает .tar.gzзагружаемые файлы, а затем автоматически распаковывает их, но имеет ограничение 10 МБ (10000 КБ) на файл (поэтому, в частности, каждый файл должен быть распакован сам по себе). Если я tar.gzвсе эти файлы, результирующий файл составляет около 72 МБ.

Я хотел бы создать восемь .tar.gzфайлов, каждый из которых имеет размер / размер (строго) меньше 10000 КБ.

В качестве альтернативы можно предположить, что все приведенные выше файлы имеют примерно одинаковое измерение, поэтому я хотел бы создать восемь .tar.gzфайлов с более или менее одинаковым количеством файлов в каждом.

Как я могу выполнить любую из этих двух задач?

Я совершенно в порядке с решением, которое включает в себя GUI, CLI или сценарии. Я не ищу скорость здесь, мне просто нужно это сделать.

dadexix86
источник
Предположительно у 12k-файлов, которые у вас есть, будут имена или повторяющиеся символы в именах. Вы могли бы tarих, добавив все файлы, начиная с определенного шаблона, пока у вас их нет. Это может быть легко написано в сценарии, но не гарантирует, что размер будет меньше, чем 9 МБ, как вам нужно. Однако вы можете вручную отрегулировать размер слишком больших файлов, разделив их дальше.
Хуан Антонио

Ответы:

9

Абсолютно лоскутная работа и быстрый, грубый набросок, как он есть, но протестированный на каталоге с 3000 файлами, приведенный ниже скрипт проделал чрезвычайно быструю работу:

#!/usr/bin/env python3
import subprocess
import os
import sys

splitinto = 2

dr = sys.argv[1]
os.chdir(dr)

files = os.listdir(dr)
n_files = len(files)
size = n_files // splitinto

def compress(tar, files):
    command = ["tar", "-zcvf", "tarfile" + str(tar) + ".tar.gz", "-T", "-", "--null"]
    proc = subprocess.Popen(command, stdin=subprocess.PIPE)
    with proc:
        proc.stdin.write(b'\0'.join(map(str.encode, files)))
        proc.stdin.write(b'\0')
    if proc.returncode:
        sys.exit(proc.returncode)

sub = []; tar = 1
for f in files:
    sub.append(f)
    if len(sub) == size:
        compress(tar, sub)
        sub = []; tar += 1

if sub:
    # taking care of left
    compress(tar, sub)

Как пользоваться

  • Сохраните его в пустой файл как compress_split.py
  • В разделе head укажите количество файлов для сжатия. На практике всегда найдется еще один, чтобы позаботиться об оставшихся «оставшихся кадрах».
  • Запустите его с каталогом с вашими файлами в качестве аргумента:

    python3 /path/tocompress_split.py /directory/with/files/tocompress

Нумерованные .tar.gzфайлы будут создаваться в том же каталоге, где находятся файлы.

объяснение

Сценарий:

  • перечисляет все файлы в каталоге
  • CD в ​​каталог, чтобы предотвратить добавление информации о пути в файл tar
  • читает список файлов, группируя их по заданному разделению
  • сжимает подгруппу (ы) в пронумерованные файлы

РЕДАКТИРОВАТЬ

Автоматически создавать куски по размеру в мб

Более сложным является использование максимального размера (в мб) кусков в качестве (второго) аргумента. В приведенном ниже сценарии чанки записываются в сжатый файл, как только чанк достигает (проходит) пороговое значение.

Поскольку сценарий запускается порциями, превышающими пороговое значение, это будет работать только в том случае, если размер (всех) файлов существенно меньше размера порции.

Сценарий:

#!/usr/bin/env python3
import subprocess
import os
import sys

dr = sys.argv[1]
chunksize = float(sys.argv[2])
os.chdir(dr)

files = os.listdir(dr)
n_files = len(files)

def compress(tar, files):
    command = ["tar", "-zcvf", "tarfile" + str(tar) + ".tar.gz", "-T", "-", "--null"]
    proc = subprocess.Popen(command, stdin=subprocess.PIPE)
    with proc:
        proc.stdin.write(b'\0'.join(map(str.encode, files)))
        proc.stdin.write(b'\0')
    if proc.returncode:
        sys.exit(proc.returncode)

sub = []; tar = 1; subsize = 0
for f in files:
    sub.append(f)
    subsize = subsize + (os.path.getsize(f)/1000000)
    if subsize >= chunksize:
        compress(tar, sub)
        sub = []; tar += 1; subsize = 0

if sub:
    # taking care of left
    compress(tar, sub)

Бежать:

python3 /path/tocompress_split.py /directory/with/files/tocompress chunksize

... где chunksize - это размер ввода для команды tar.

В этом предложены улучшения, предложенные @DavidFoerster. Спасибо большое !

Якоб Влейм
источник
@ dadexix86 пожалуйста!
Джейкоб Влейм
Я избавился от вызова оболочки и использовал список аргументов напрямую. Тем не менее, большие списки аргументов могут быть проблематичными, и я постараюсь еще больше улучшить tarвызов, предоставив список файлов в стандартном потоке ввода.
Дэвид Фёрстер
Привет @DavidFoerster, я доверяю твоему пониманию, но в чем преимущество?
Джейкоб Влейм
Большинство сред выполнения имеют (мягкое и жесткое) ограничение на общую длину строк аргументов команды, которое вы быстро достигнете при работе с тысячами файлов. Вот почему tarпозволяет вам указать файлы для добавления (или извлечения) при стандартном вводе с соответствующей опцией.
Дэвид Фёрстер
@DavidFoerster, есть проблема, но вторая больше не работает. На самом деле ни один из них не делает ...
Джейкоб Влейм
6

Чистый подход к оболочке:

files=(*); 
num=$((${#files[@]}/8));
k=1
for ((i=0; i<${#files[@]}; i+=$num)); do 
    tar cvzf files$k.tgz -- "${files[@]:$i:$num}"
    ((k++))
done

объяснение

  • files=(*): сохранить список файлов (также каталогов, если таковые имеются, измените на, files=(*.txt)чтобы получить только вещи с txtрасширением) в массиве $files.
  • num=$((${#files[@]}/8));: ${#files[@]}количество элементов в массиве $files. Это $(( ))bash (ограниченный) способ выполнения арифметики. Итак, эта команда устанавливает $numколичество файлов, разделенное на 8.
  • k=1 : просто счетчик, чтобы назвать тарболы.
  • for ((i=0; i<${#files[@]}; i+=$num)); do: перебрать значения массива. $iинициализируется в 0(первый элемент массива) и увеличивается на $num. Это продолжается до тех пор, пока мы не пройдем все элементы (файлы).
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}: в bash вы можете получить срез массива (часть массива), используя ${array[@]:start:length}, поэтому ${array[@]:2:3}вернет три элемента, начиная со второго. Здесь мы берем фрагмент, который начинается с текущего значения $iи имеет $numдлину элемента. --Требуется в случае , если какой - либо из ваших имен файлов может начинаться с -.
  • ((k++)) : приращение $k
terdon
источник
Ницца! Впервые я увидел практическое использование диапазонов индексов массива bash.
Джо
Очень чистый и лаконичный. Для меня это более понятно, чем решения Python, хотя оба довольно хороши. Интересно, как все они сравниваются по производительности?
DocSalvager