Cách tar.gz nhiều tệp có kích thước tương tự vào nhiều kho lưu trữ với giới hạn kích thước


11

Tôi đang dùng Ubuntu 16.04.

Tôi có một thư mục chứa rất nhiều tệp văn bản (gần 12k). Tôi cần tải tất cả chúng lên một trang web chấp nhận .tar.gztải lên và sau đó tự động giải nén chúng, nhưng có giới hạn 10MB (10000KB) cho mỗi tệp (đặc biệt là mỗi tệp phải tự giải nén). Nếu tôi tar.gztất cả các tệp này, tệp kết quả là khoảng 72 MB.

Những gì tôi muốn làm là tạo tám .tar.gztệp, mỗi tệp có kích thước / kích thước (đúng) nhỏ hơn 10000KB.

Ngoài ra, người ta có thể giả sử rằng tất cả các tệp ở trên có cùng một kích thước, vì vậy tôi muốn tạo tám .tar.gztệp với số lượng tệp nhiều hơn hoặc ít hơn cùng một tệp.

Làm thế nào tôi có thể làm bất kỳ trong hai nhiệm vụ này?

Tôi hoàn toàn ổn với giải pháp liên quan đến GUI, CLI hoặc script. Tôi không tìm kiếm tốc độ ở đây, tôi chỉ cần nó được thực hiện.


Có lẽ các tệp 12k bạn có sẽ có các mẫu hoặc ký tự lặp lại trong tên của chúng. Bạn có thể có thể tarchúng bằng cách thêm tất cả các tệp bắt đầu bằng một mẫu nhất định cho đến khi bạn có tất cả. Điều này có thể dễ dàng được viết kịch bản nhưng không đảm bảo kích thước sẽ thấp hơn 9MB khi bạn cần. Tuy nhiên, bạn có thể điều chỉnh thủ công kích thước của các tệp quá lớn bằng cách chia nhỏ hơn nữa.
Juan Antonio

Câu trả lời:


9

Hoàn toàn chắp vá và một bản phác thảo nhanh, thô, nhưng đã được thử nghiệm trên một thư mục với 3000 tệp, tập lệnh dưới đây đã thực hiện một công việc cực kỳ nhanh chóng:

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

Cách sử dụng

  • Lưu nó vào một tập tin trống như compress_split.py
  • Trong phần đầu, đặt số lượng tệp cần nén vào. Trong thực tế, sẽ luôn có thêm một người để chăm sóc một số "còn lại" còn lại.
  • Chạy nó với thư mục với các tệp của bạn làm đối số:

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

.tar.gzcác tệp được đánh số sẽ được tạo trong cùng thư mục với vị trí của các tệp.

Giải trình

Kịch bản:

  • liệt kê tất cả các tập tin trong thư mục
  • cd vào thư mục để ngăn thêm thông tin đường dẫn vào tệp tar
  • đọc qua danh sách tập tin, nhóm chúng theo bộ chia
  • nén (các) nhóm phụ vào các tệp được đánh số

BIÊN TẬP

Tự động tạo khối theo kích thước tính bằng mb

Tinh vi hơn là sử dụng kích thước tối đa (tính bằng mb) của các khối làm đối số (giây). Trong đoạn script bên dưới, các đoạn được ghi vào một tệp nén ngay khi khối này đạt đến (vượt qua) ngưỡng.

Do tập lệnh được kích hoạt bởi các đoạn, vượt quá ngưỡng, nên tập lệnh này sẽ chỉ hoạt động nếu kích thước của (tất cả) tệp nhỏ hơn đáng kể so với kích thước khối.

Kịch bản:

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

Chạy:

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

... Trong đó chunksize là kích thước của đầu vào cho lệnh tar.

Trong phần này, các cải tiến được đề xuất bởi @DavidFoerster được bao gồm. Cảm ơn rất nhiều !


@ Dadexix86 bạn được chào đón!
Jacob Vlijm

Tôi thần thoát khỏi lời gọi shell và sử dụng một danh sách đối số trực tiếp. Tuy nhiên, danh sách đối số lớn có thể có vấn đề và tôi sẽ cố gắng cải thiện việc targọi thêm bằng cách cung cấp danh sách tệp trên luồng đầu vào tiêu chuẩn.
David Foerster

Xin chào @DavidFoerster, tôi tin tưởng vào cái nhìn sâu sắc của bạn, nhưng lợi thế là gì?
Jacob Vlijm

Hầu hết các môi trường thời gian chạy đều có giới hạn (mềm và cứng) trên tổng độ dài của chuỗi đối số của lệnh mà bạn sẽ tiếp cận nhanh khi hoạt động trên hàng ngàn tệp. Đó là lý do tại sao tarcho phép bạn chỉ định các tệp để thêm (hoặc trích xuất) trên đầu vào tiêu chuẩn với một tùy chọn phù hợp.
David Foerster

@DavidFoerster có một vấn đề mặc dù, cái thứ hai không chạy nữa. Thật ra không ai trong số họ làm ...
Jacob Vlijm

6

Một cách tiếp cận vỏ tinh khiết:

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

Giải trình

  • files=(*): lưu danh sách các tệp (cũng là thư mục nếu có, thay đổi thành files=(*.txt)chỉ lấy những thứ có txtphần mở rộng) trong mảng $files.
  • num=$((${#files[@]}/8));: ${#files[@]}là số phần tử trong mảng $files. Cách $(( ))làm số học (giới hạn) của bash là số học. Vì vậy, lệnh này đặt $numthành số lượng tệp chia cho 8.
  • k=1 : chỉ là một bộ đếm để đặt tên cho tarballs.
  • for ((i=0; i<${#files[@]}; i+=$num)); do: lặp qua các giá trị của mảng. $iđược khởi tạo tại 0(phần tử đầu tiên của mảng) và tăng theo $num. Điều này tiếp tục cho đến khi chúng tôi đi qua tất cả các yếu tố (tập tin).
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}: trong bash, bạn có thể lấy một lát mảng (một phần của mảng) bằng cách sử dụng ${array[@]:start:length}, Vì vậy, ${array[@]:2:3}sẽ trả về ba phần tử bắt đầu từ phần thứ hai. Ở đây, chúng tôi đang lấy một lát bắt đầu từ giá trị hiện tại $ivà là $numcác phần tử dài. Điều --này là cần thiết trong trường hợp bất kỳ tên tệp nào của bạn có thể bắt đầu bằng a -.
  • ((k++)) : tăng $k

Đẹp! Lần đầu tiên tôi thấy việc sử dụng thực tế các phạm vi chỉ số mảng bash.
Joe

Rất sạch sẽ và cô đọng. Đối với tôi, dễ hiểu hơn các giải pháp Python mặc dù cả hai đều khá tốt. Tự hỏi làm thế nào tất cả họ so sánh trong hiệu suất?
DocSalvager
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.