Як tar.gz багато файлів подібного розміру в декілька архівів з обмеженням розміру


11

Я на Ubuntu 16.04.

У мене папка з великою кількістю текстових файлів (майже 12 к). Мені потрібно завантажити їх усіх на веб-сайт, який приймає .tar.gzзавантаження, а потім автоматично розпаковує їх, але має обмеження в 10 МБ (10000 КБ) на файл (тому, зокрема, кожен файл повинен бути декомпресований самостійно). Якщо у мене tar.gzвсі ці файли, то отриманий файл становить приблизно 72 Мб.

Що я хотів би зробити, це створити вісім .tar.gzфайлів, кожен розмір / розмір (суворо) менше 10000 КБ.

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

Як я можу виконати будь-яке з цих двох завдань?

Мені ідеально підходить рішення, яке передбачає графічний інтерфейс, CLI або сценарій. Я не шукаю тут швидкості, я просто потребую цього зробити.


Імовірно, у ваших файлах 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
  • У головному розділі встановіть кількість файлів для стискання. На практиці завжди знайдеться ще один, щоб подбати про решту кількох "лівих кадрів".
  • Запустіть його в каталозі з файлами як аргумент:

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

нумеровані .tar.gzфайли будуть створені в тому самому каталозі, де і файли.

Пояснення

Сценарій:

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

EDIT

Автоматично створювати шматки за розміром в mb

Більш складним є використання максимального розміру (в mb) фрагментів як (другого) аргументу. У нижченаведеному сценарії шматки записуються в стислий файл, як тільки шматок досягає (переходить) поріг.

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

Сценарій:

#!/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

Приємно! Вперше я побачив практичне використання діапазонів індексу bash-масивів.
Джо

Дуже чистий і лаконічний. Для мене більш зрозумілі, ніж рішення Python, хоча обидва є досить хорошими. Цікаво, як вони всі порівнюються за продуктивністю?
DocSalvager
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.