วิธี tar.gz ไฟล์ที่มีขนาดใกล้เคียงกันเป็นหลายไฟล์ในคลังเก็บหลายไฟล์ด้วยขนาดที่ จำกัด


11

ฉันใช้ Ubuntu 16.04

ฉันมีโฟลเดอร์ที่มีไฟล์ข้อความจำนวนมาก (เกือบ 12k) ฉันต้องอัปโหลดพวกเขาทั้งหมดไปยังเว็บไซต์ที่ยอมรับการ.tar.gzอัปโหลดแล้วคลายบีบอัดโดยอัตโนมัติ แต่มีขีด จำกัด 10MB (10,000KB) ต่อไฟล์ (ดังนั้นโดยเฉพาะอย่างยิ่งแต่ละไฟล์จะต้องแตกไฟล์ด้วยตัวเอง) หากฉันtar.gzไฟล์เหล่านี้ทั้งหมดไฟล์ที่ได้จะมีขนาดประมาณ 72MB

สิ่งที่ฉันต้องการทำคือการสร้าง.tar.gzไฟล์แปดไฟล์แต่ละขนาด / มิติ (อย่างเคร่งครัด) มีขนาดเล็กกว่า 10,000KB

อีกวิธีหนึ่งอาจสันนิษฐานได้ว่าไฟล์ทั้งหมดข้างต้นมีขนาดประมาณเดียวกันดังนั้นฉันต้องการสร้าง.tar.gzไฟล์แปดไฟล์โดยมีจำนวนไฟล์เท่ากัน

ฉันจะทำงานสองอย่างใดอย่างหนึ่งเหล่านี้ได้อย่างไร

ฉันใช้งานได้ดีกับโซลูชันที่เกี่ยวข้องกับ GUI, CLI หรือการเขียนสคริปต์ ฉันไม่ได้มองหาความเร็วที่นี่ฉันต้องการมันเสร็จแล้ว


สันนิษฐานว่าไฟล์ 12k ที่คุณมีจะมีรูปแบบหรืออักขระซ้ำ ๆ กันในชื่อของมัน คุณสามารถทำได้tarโดยการเพิ่มไฟล์ทั้งหมดที่เริ่มต้นด้วยรูปแบบที่แน่นอนจนกว่าคุณจะมีพวกเขาทั้งหมด สามารถเขียนสคริปต์ได้อย่างง่ายดาย แต่ไม่รับประกันว่าขนาดจะต่ำกว่า 9MB ตามที่คุณต้องการ อย่างไรก็ตามคุณสามารถปรับขนาดของไฟล์เหล่านั้นด้วยตนเองที่มีขนาดใหญ่เกินไปด้วยตนเองโดยแยกไฟล์เพิ่มเติม
Juan Antonio

คำตอบ:


9

การเย็บปะติดปะต่อกันอย่างสมบูรณ์และภาพร่างคร่าวๆที่รวดเร็วอย่างที่มันเป็น แต่การทดสอบในไดเรกทอรีที่มีไฟล์ 3,000 ไฟล์สคริปต์ด้านล่างทำงานได้อย่างรวดเร็วมาก:

#!/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's ในไดเร็กทอรีเพื่อป้องกันการเพิ่มข้อมูลพา ธ ไปยังไฟล์ tar
  • อ่านผ่านรายการไฟล์โดยจัดกลุ่มตามหมวดที่กำหนดไว้
  • บีบอัดกลุ่มย่อยลงในไฟล์ที่มีหมายเลข

แก้ไข

สร้างชิ้นโดยอัตโนมัติตามขนาดเป็น mb

ความซับซ้อนมากขึ้นคือการใช้ขนาดสูงสุด (เป็นเมกะไบต์) ของชิ้นข้อมูลเป็นอาร์กิวเมนต์ (วินาที) ในสคริปต์ด้านล่าง chunks จะถูกเขียนลงในไฟล์บีบอัดทันทีที่ chunk ถึง (ผ่าน) threshold

เนื่องจากสคริปต์ถูกทริกเกอร์โดย chunks เกินขีด จำกัด การทำเช่นนี้จะใช้ได้ก็ต่อเมื่อขนาดของไฟล์ (ทั้งหมด) มีขนาดเล็กกว่าขนาดก้อนอย่างมาก

สคริปต์:

#!/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 ยินดีต้อนรับ!
Jacob Vlijm

ฉันจะกำจัดการเรียกใช้เชลล์และใช้รายการอาร์กิวเมนต์โดยตรง ยังคงรายการอาร์กิวเมนต์ขนาดใหญ่อาจมีปัญหาและฉันจะพยายามปรับปรุงการtarเรียกใช้เพิ่มเติมโดยระบุรายการไฟล์ในสตรีมอินพุตมาตรฐาน
David Foerster

สวัสดี @DavidFoerster ฉันเชื่อใจคุณมาก แต่ข้อดีคืออะไร
Jacob Vlijm

สภาพแวดล้อมรันไทม์ส่วนใหญ่มีข้อ จำกัด (อ่อนและแข็ง) กับความยาวทั้งหมดของสตริงอาร์กิวเมนต์ของคำสั่งที่คุณจะเข้าถึงได้อย่างรวดเร็วเมื่อทำงานกับไฟล์หลายพันไฟล์ นั่นเป็นเหตุผลที่tarช่วยให้คุณระบุไฟล์ที่จะเพิ่ม (หรือแยก) ในอินพุตมาตรฐานพร้อมตัวเลือกที่เหมาะสม
David Foerster

@DavidFoerster มีปัญหา แต่ตัวที่สองไม่ทำงานอีกต่อไป ที่จริงไม่ใช่ของพวกเขาไม่ ...
จาค็อบ Vlijm

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 : เพียงเคาน์เตอร์เพื่อตั้งชื่อลูก Tarballs
  • for ((i=0; i<${#files[@]}; i+=$num)); do: วนซ้ำค่าของอาร์เรย์ $iจะเริ่มต้นที่0(องค์ประกอบแรกของอาร์เรย์) $numและเพิ่มขึ้นโดย สิ่งนี้จะดำเนินต่อไปจนกว่าเราจะผ่านองค์ประกอบทั้งหมด (ไฟล์)
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}: ใน bash คุณสามารถใช้ array slice (ส่วนหนึ่งของ array) ได้${array[@]:start:length}ดังนั้น${array[@]:2:3}จะคืนค่าองค์ประกอบสามค่าเริ่มต้นจากวินาที ที่นี่เรากำลังแยกส่วนที่เริ่มต้นที่ค่าปัจจุบันของ$iและมี$numองค์ประกอบยาว --เป็นสิ่งจำเป็นในกรณีใด ๆ -ของชื่อไฟล์ของคุณสามารถเริ่มต้นด้วย
  • ((k++)) : เพิ่มขึ้น $k

ดี! ครั้งแรกที่ฉันเห็นการใช้งานจริงของช่วงดัชนี bash array
โจ

สะอาดและรวบรัดมาก สำหรับฉันเข้าใจได้ง่ายกว่าโซลูชัน Python แม้ว่าทั้งคู่จะค่อนข้างดี สงสัยว่าพวกเขาเปรียบเทียบประสิทธิภาพอย่างไร
DocSalvager
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.