Cara tar.gz banyak file berukuran serupa ke banyak arsip dengan batas ukuran

11

Saya di Ubuntu 16.04.

Saya memiliki folder dengan banyak file teks (hampir 12k). Saya perlu mengunggah semuanya ke situs web yang menerima .tar.gzunggahan dan kemudian mendekompresinya secara otomatis, tetapi memiliki batas 10MB (10.000KB) per file (jadi khususnya setiap file harus didekompresi sendiri). Jika saya tar.gzsemua file ini, file yang dihasilkan adalah sekitar 72MB.

Yang ingin saya lakukan adalah membuat delapan .tar.gzfile, masing-masing ukuran / dimensi (ketat) lebih kecil dari 10000KB.

Atau, kita dapat mengasumsikan bahwa semua file di atas memiliki dimensi yang kira-kira sama, jadi saya ingin membuat delapan .tar.gzfile dengan masing-masing jumlah file yang kurang lebih sama.

Bagaimana saya bisa melakukan salah satu dari dua tugas ini?

Saya baik-baik saja dengan solusi yang melibatkan GUI, CLI atau scripting. Saya tidak mencari kecepatan di sini, saya hanya perlu dilakukan.

dadexix86
sumber
Agaknya file 12k yang Anda miliki akan memiliki pola atau karakter berulang dalam namanya. Anda dapat tarmelakukannya dengan menambahkan semua file mulai dengan pola tertentu hingga Anda memiliki semuanya. Ini dapat dengan mudah dituliskan tetapi tidak menjamin ukurannya akan lebih rendah dari 9MB yang Anda butuhkan. Anda bisa, bagaimanapun, secara manual menyesuaikan ukuran file-file yang terlalu besar dengan membaginya lebih lanjut.
Juan Antonio

Jawaban:

9

Sepenuhnya tambal sulam dan sketsa kasar yang cepat, tetapi diuji pada direktori dengan 3000 file, skrip di bawah ini melakukan pekerjaan yang sangat cepat:

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

Cara Penggunaan

  • Simpan ke file kosong sebagai compress_split.py
  • Di bagian kepala, atur jumlah file yang akan dikompres. Dalam praktiknya, akan selalu ada satu lagi untuk mengurus beberapa "sisa overs" yang tersisa.
  • Jalankan dengan direktori dengan file Anda sebagai argumen:

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

.tar.gzfile bernomor akan dibuat di direktori yang sama dengan tempat file tersebut.

Penjelasan

Naskah:

  • daftar semua file dalam direktori
  • cd ke dalam direktori untuk mencegah penambahan info jalur ke file tar
  • membaca melalui daftar file, mengelompokkannya berdasarkan divisi yang ditetapkan
  • kompres sub grup menjadi file bernomor

EDIT

Secara otomatis membuat potongan berdasarkan ukuran dalam mb

Lebih canggih adalah dengan menggunakan ukuran maksimum (dalam mb) dari potongan sebagai argumen (kedua). Dalam skrip di bawah ini, potongan ditulis ke dalam file terkompresi begitu potongan mencapai (melewati) ambang batas.

Karena skrip dipicu oleh chunk, melebihi ambang, ini hanya akan berfungsi jika ukuran (semua) file secara substansial lebih kecil dari ukuran chunk.

Naskah:

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

Untuk berlari:

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

... di mana chunksize adalah ukuran input untuk perintah tar.

Dalam hal ini, perbaikan yang disarankan oleh @DavidFoerster disertakan. Terima kasih banyak !

Yakub Vlijm
sumber
@ dadexix86 sama-sama
Jacob Vlijm
Saya menyingkirkan doa shell dan menggunakan daftar argumen secara langsung. Namun, daftar argumen yang besar mungkin bermasalah dan saya akan mencoba meningkatkan tarpermintaan lebih lanjut dengan memberikan daftar file pada aliran input standar.
David Foerster
Hai @ DavidFoerster, saya percaya wawasan Anda, tapi apa keuntungannya?
Jacob Vlijm
Sebagian besar lingkungan runtime memiliki batas (lunak dan keras) pada panjang total string argumen dari sebuah perintah yang akan Anda jangkau dengan cepat ketika beroperasi pada ribuan file. Itu sebabnya tarAnda dapat menentukan file untuk ditambahkan (atau diekstrak) pada input standar dengan opsi yang sesuai.
David Foerster
@ DavidVoerster ada masalah, yang kedua tidak berjalan lagi. Sebenarnya tidak satu pun dari mereka ...
Jacob Vlijm
6

Pendekatan shell murni:

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

Penjelasan

  • files=(*): simpan daftar file (juga direktori jika ada, ubah files=(*.txt)untuk mendapatkan hanya hal-hal dengan txtekstensi) dalam array $files.
  • num=$((${#files[@]}/8));: ${#files[@]}adalah jumlah elemen dalam array $files. Ini $(( ))adalah cara bash (terbatas) dalam melakukan aritmatika. Jadi, perintah ini mengatur $numjumlah file dibagi 8.
  • k=1 : hanya penghitung untuk memberi nama tarbal.
  • for ((i=0; i<${#files[@]}; i+=$num)); do: beralih di atas nilai-nilai array. $idiinisialisasi pada 0(elemen pertama array) dan ditambahkan oleh $num. Ini berlanjut sampai kita telah melalui semua elemen (file).
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}: dalam bash, Anda bisa mendapatkan irisan array (bagian dari array) menggunakan ${array[@]:start:length}, Jadi ${array[@]:2:3}akan mengembalikan tiga elemen mulai dari yang kedua. Di sini, kami mengambil potongan yang dimulai pada nilai saat ini $idan merupakan $numelemen yang panjang. The --diperlukan dalam hal apapun dari nama file Anda dapat memulai dengan -.
  • ((k++)) : kenaikan $k
terdon
sumber
Bagus! Pertama kali saya melihat penggunaan praktis rentang indeks bash array.
Joe
Sangat bersih dan ringkas. Bagi saya, lebih bisa dimengerti daripada solusi Python meskipun keduanya cukup bagus. Bertanya-tanya bagaimana mereka semua membandingkan dalam kinerja?
DocSalvager