Как tar.gz много файлов одинакового размера в несколько архивов с ограничением размера
Я на Ubuntu 16.04.
У меня есть папка с большим количеством текстовых файлов (почти 12k). Мне нужно загрузить их все на сайт, который принимает .tar.gz
загружает, а затем распаковывает их автоматически, но имеет ограничение 10 МБ (10000 КБ) на файл (поэтому, в частности, каждый файл должен быть распакован сам по себе). Если я tar.gz
все эти файлы в результате около 72 МБ.
Что я хотел бы сделать, это создать восемь .tar.gz
файлы, каждый из которых имеет размер / размер (строго) менее 10000 КБ.
В качестве альтернативы можно предположить, что все приведенные выше файлы имеют примерно одинаковое измерение, поэтому я хотел бы создать восемь .tar.gz
файлы с более или менее одинаковым количеством файлов каждый.
Как я могу выполнить любую из этих двух задач?
Я совершенно в порядке с решением, которое включает в себя GUI, CLI или сценарии. Я не ищу скорость здесь, мне просто нужно это сделать.
2 ответа
Абсолютно лоскутная работа и быстрый, грубый набросок, как он есть, но протестированный на каталоге с 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
- В разделе заголовка установите количество файлов для сжатия. На практике всегда будет еще один, чтобы позаботиться об оставшихся немногих "остатках".
Запустите его с каталогом с вашими файлами в качестве аргумента:
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
... где chunk size - это размер ввода для команды tar.
В этом предложены улучшения, предложенные @DavidFoerster. Большое спасибо!
Чистый подход оболочки:
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