Jak tar.gz wielu plików o podobnej wielkości do wielu archiwów z ograniczeniem rozmiaru

11

Jestem na Ubuntu 16.04.

Mam folder z dużą ilością plików tekstowych (prawie 12 KB). Muszę przesłać je wszystkie na stronę internetową, która akceptuje .tar.gzprzesyłanie, a następnie automatycznie je dekompresuje, ale ma limit 10 MB (10000 KB) na plik (w szczególności każdy plik musi zostać zdekompresowany osobno). Jeśli tar.gzwszystkie te pliki, plik wynikowy ma około 72 MB.

Chciałbym utworzyć osiem .tar.gzplików, każdy o rozmiarze / wymiarze (ściśle) mniejszym niż 10000 KB.

Alternatywnie można założyć, że wszystkie powyższe pliki mają w przybliżeniu ten sam wymiar, dlatego chciałbym utworzyć osiem .tar.gzplików z mniej więcej taką samą ilością plików.

Jak mogę wykonać którekolwiek z tych dwóch zadań?

Nie mam nic przeciwko rozwiązaniu, które obejmuje GUI, CLI lub skrypty. Nie szukam tutaj prędkości, po prostu muszę to zrobić.

dadexix86
źródło
Prawdopodobnie 12k plików, które posiadasz, będzie miało wzorce lub powtarzające się znaki w swoich nazwach. Możesz tarje dodać, dodając wszystkie pliki, zaczynając od określonego wzorca, aż uzyskasz je wszystkie. Można to łatwo skrypty, ale nie gwarantuje, że rozmiar będzie mniejszy niż 9 MB, jak potrzebujesz. Można jednak ręcznie dostosować rozmiar tych plików, które są zbyt duże, dzieląc je dalej.
Juan Antonio

Odpowiedzi:

9

Całkowicie patchwork i szybki, szorstki szkic, ale testowany w katalogu z 3000 plików, poniższy skrypt wykonał niezwykle szybką pracę:

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

Jak używać

  • Zapisz go w pustym pliku jako compress_split.py
  • W sekcji nagłówka ustaw liczbę plików do skompresowania. W praktyce zawsze pozostanie jeszcze jeden, który zajmie się pozostałymi kilkoma „resztkami”.
  • Uruchom go z katalogiem z plikami jako argumentem:

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

.tar.gzpliki ponumerowane zostaną utworzone w tym samym katalogu, w którym znajdują się pliki.

Wyjaśnienie

Scenariusz:

  • wyświetla wszystkie pliki w katalogu
  • cd do katalogu, aby zapobiec dodaniu informacji o ścieżce do pliku tar
  • czyta listę plików, grupując je według podziału zestawu
  • kompresuje podgrupy w pliki ponumerowane

EDYTOWAĆ

Automatycznie twórz porcje według rozmiaru w MB

Bardziej wyrafinowane jest użycie maksymalnego rozmiaru (w mb) porcji jako (drugiego) argumentu. W skrypcie poniżej fragmenty są zapisywane w skompresowanym pliku, gdy tylko fragment osiągnie (przekroczy) próg.

Ponieważ skrypt jest uruchamiany przez porcje, przekraczając próg, zadziała to tylko wtedy, gdy rozmiar (wszystkich) plików jest znacznie mniejszy niż porcja.

Scenariusz:

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

Biegać:

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

... gdzie chunksize to rozmiar danych wejściowych dla polecenia tar.

W tym zawarte są sugerowane ulepszenia @DavidFoerster. Dzięki dużo !

Jacob Vlijm
źródło
@ dadexix86 nie ma za co!
Jacob Vlijm,
Pozbyłem się wywołania powłoki i użyłem bezpośrednio listy argumentów. Mimo to duże listy argumentów mogą być problematyczne i postaram się ulepszyć tarwywołanie, podając listę plików w standardowym strumieniu wejściowym.
David Foerster,
Cześć @DavidFoerster, ufam twojemu wglądowi, ale jaka jest zaleta?
Jacob Vlijm,
Większość środowisk wykonawczych ma (miękki i twardy) limit całkowitej długości ciągów argumentów polecenia, do którego można szybko dotrzeć podczas pracy na tysiącach plików. Dlatego tarpozwala określić pliki do dodania (lub wyodrębnienia) na standardowym wejściu z odpowiednią opcją.
David Foerster,
@DavidFoerster jest jednak problem, drugi już nie działa. W rzeczywistości żaden z nich nie ...
Jacob Vlijm,
6

Podejście oparte na czystej powłoce:

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

Wyjaśnienie

  • files=(*): zapisz listę plików (także katalogi, jeśli są obecne, zmień, files=(*.txt)aby uzyskać tylko rzeczy z txtrozszerzeniem) w tablicy $files.
  • num=$((${#files[@]}/8));: ${#files[@]}to liczba elementów w tablicy $files. Jest $(( ))to (ograniczony) sposób wykonywania arytmetyki przez bash. To polecenie ustawia $numliczbę plików podzieloną przez 8.
  • k=1 : tylko licznik do nazwania tarballów.
  • for ((i=0; i<${#files[@]}; i+=$num)); do: iteruj po wartościach tablicy. $ijest inicjowany o 0(pierwszy element tablicy) i zwiększany o $num. Trwa to, dopóki nie przejdziemy przez wszystkie elementy (pliki).
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}: w bash można uzyskać plasterek tablicy (część tablicy) za pomocą ${array[@]:start:length}, więc ${array[@]:2:3}zwróci trzy elementy, zaczynając od drugiego. Tutaj bierzemy wycinek, który zaczyna się od bieżącej wartości $ii ma $numdługość elementów. Jest --to konieczne, jeśli dowolna z nazw plików może zaczynać się od -.
  • ((k++)) : przyrost $k
terdon
źródło
Miły! Po raz pierwszy widziałem praktyczne zastosowanie zakresów indeksu tablicy bash.
Joe
Bardzo czysty i zwięzły. Dla mnie bardziej zrozumiałe niż rozwiązania Python, choć oba są całkiem dobre. Zastanawiasz się, jak wszystkie różnią się pod względem wydajności?
DocSalvager,