ต่อท้ายไฟล์ขนาดใหญ่ต่อกันโดยไม่ต้องคัดลอก


41

มีไฟล์ขนาดใหญ่ 5 ไฟล์ (file1, file2, .. file5) ประมาณ 10G แต่ละไฟล์และมีพื้นที่ว่างเหลือน้อยมากเหลือน้อยบนดิสก์และฉันต้องการเชื่อมไฟล์ทั้งหมดนี้ให้เป็นไฟล์เดียว ไม่จำเป็นต้องเก็บไฟล์ต้นฉบับไว้เพียงไฟล์สุดท้ายเท่านั้น

การต่อข้อมูลตามปกติจะเกิดขึ้นcatตามลำดับสำหรับไฟล์file2.. file5:

cat file2 >> file1 ; rm file2

น่าเสียดายที่วิธีนี้ต้องการพื้นที่ว่างอย่างน้อย 10G ที่ฉันไม่มี มีวิธีการเชื่อมไฟล์โดยไม่ต้องคัดลอกจริง แต่บอกระบบไฟล์อย่างใดที่ file1 ไม่ได้จบที่ file1 เดิมและดำเนินการต่อที่ file2 เริ่ม?

PS ระบบไฟล์เป็น ext4 ถ้าเรื่องนั้นสำคัญ


2
ฉันสนใจที่จะดูวิธีแก้ปัญหา แต่ฉันสงสัยว่ามันเป็นไปไม่ได้หากไม่ได้ยุ่งกับระบบไฟล์โดยตรง
Kevin

1
ทำไมคุณต้องมีฟิสิคัลไฟล์เดียวที่มีขนาดใหญ่มาก ฉันกำลังถามเพราะบางทีคุณสามารถหลีกเลี่ยงการต่อข้อมูลซึ่งเป็นคำตอบปัจจุบันที่น่ารำคาญ
liori

6
@rush: คำตอบนี้อาจช่วยได้: serverfault.com/a/487692/16081
liori

1
ทางเลือกให้กับอุปกรณ์ mapper มีประสิทธิภาพน้อยลง แต่ง่ายต่อการใช้และผลในอุปกรณ์ partitionable และสามารถใช้จากเครื่องระยะไกลคือการใช้โหมด "หลาย" nbd-serverของ
Stéphane Chazelas

1
พวกเขามักจะเรียกฉันว่าโง่เมื่อฉันบอกว่าฉันคิดว่ามันควรจะเย็น
n611x007

คำตอบ:


19

AFAIK เป็นไปไม่ได้ที่จะตัดทอนไฟล์ตั้งแต่ต้น (อาจเป็นจริงสำหรับเครื่องมือมาตรฐาน แต่สำหรับระดับ syscall ดูที่นี่ ) แต่ด้วยการเพิ่มความซับซ้อนบางอย่างคุณสามารถใช้การตัดแบบปกติ (รวมถึงไฟล์แบบกระจาย): คุณสามารถเขียนไปยังจุดสิ้นสุดของไฟล์เป้าหมายโดยไม่ต้องเขียนข้อมูลทั้งหมดในระหว่างนั้น

สมมติว่าทั้งสองไฟล์เป็น 5GiB (5120 MiB) และคุณต้องการย้ายครั้งละ 100 MiB คุณรันลูปซึ่งประกอบด้วย

  1. คัดลอกหนึ่งบล็อกจากจุดสิ้นสุดของไฟล์ต้นฉบับไปยังจุดสิ้นสุดของไฟล์เป้าหมาย (เพิ่มพื้นที่ดิสก์ที่ใช้)
  2. การตัดไฟล์ต้นฉบับด้วยหนึ่งบล็อก (การเพิ่มพื้นที่ว่างในดิสก์)

    for((i=5119;i>=0;i--)); do
      dd if=sourcefile of=targetfile bs=1M skip="$i" seek="$i" count=1
      dd if=/dev/zero of=sourcefile bs=1M count=0 seek="$i"
    done
    

แต่ให้ลองกับไฟล์ทดสอบที่เล็กกว่าก่อนโปรด ...

อาจเป็นไฟล์ที่มีขนาดเท่ากันหรือทวีคูณของขนาดบล็อกไม่ได้ ในกรณีดังกล่าวการคำนวณออฟเซ็ตมีความซับซ้อนมากขึ้น seek_bytesและskip_bytesควรจะใช้แล้ว

หากนี่คือวิธีที่คุณต้องการไป แต่ต้องการความช่วยเหลือสำหรับรายละเอียดโปรดถามอีกครั้ง

การเตือน

ขึ้นอยู่กับddขนาดบล็อกไฟล์ที่ได้จะเป็นฝันร้ายที่กระจัดกระจาย


ดูเหมือนว่านี่เป็นวิธีที่ได้รับการยอมรับมากที่สุดในการต่อไฟล์เข้าด้วยกัน ขอบคุณสำหรับคำแนะนำ.
เร่ง

3
หากไม่มีไฟล์สนับสนุนกระจัดกระจายคุณสามารถบล็อกไฟล์ที่สองในตำแหน่งที่ถูกบล็อกได้แล้วลบบล็อกสุดท้ายและเพิ่มลงในไฟล์ที่สอง
ratchet freak

1
ฉันไม่ได้ลองด้วยตัวเอง (แต่ฉันกำลังจะทำ) แต่seann.herdejurgen.com/resume/samag.com/html/v09/i08/a9_l1.htmเป็นสคริปต์ Perl ที่อ้างว่าใช้อัลกอริธึมนี้
zwol

16

แทนที่จะทำการรวมไฟล์เข้าด้วยกันเป็นไฟล์เดียวอาจจำลองไฟล์เดียวด้วยไพพ์ที่มีชื่อหากโปรแกรมของคุณไม่สามารถจัดการไฟล์หลายไฟล์ได้

mkfifo /tmp/file
cat file* >/tmp/file &
blahblah /tmp/file
rm /tmp/file

ดังที่ Hauke ​​แนะนำ losetup / dmsetup สามารถทำงานได้เช่นกัน การทดสอบอย่างรวดเร็ว; ฉันสร้าง 'file1..file4' และด้วยความพยายาม:

for i in file*;do losetup -f ~/$i;done

numchunks=3
for i in `seq 0 $numchunks`; do
        sizeinsectors=$((`ls -l file$i | awk '{print $5}'`/512))
        startsector=$(($i*$sizeinsectors))
        echo "$startsector $sizeinsectors linear /dev/loop$i 0"
done | dmsetup create joined

จากนั้น / dev / dm-0 มีอุปกรณ์บล็อกเสมือนที่มีไฟล์ของคุณเป็นเนื้อหา

ฉันยังไม่ได้ทดสอบอย่างนี้

การแก้ไขอื่น: ขนาดไฟล์ต้องหารด้วย 512 อย่างสม่ำเสมอหรือคุณจะสูญเสียข้อมูลบางส่วน ถ้าเป็นเช่นนั้นคุณก็เป็นคนดี ฉันเห็นเขายังตั้งข้อสังเกตว่าด้านล่าง


เป็นความคิดที่ดีที่จะอ่านไฟล์นี้หนึ่งครั้งน่าเสียดายที่ไม่มีความสามารถในการกระโดดข้ามไปข้างหลัง / ข้างหน้า
เร่ง

7
@rush ทางเลือกที่ดีกว่าอาจใส่อุปกรณ์วนรอบในแต่ละไฟล์และรวมเข้าdmsetupกับอุปกรณ์บล็อกเสมือน (ซึ่งอนุญาตการดำเนินการค้นหาปกติ แต่ไม่ผนวกหรือตัดทอน) หากขนาดของไฟล์แรกไม่ใช่ขนาด 512 จากนั้นคุณควรคัดลอกเซกเตอร์สุดท้ายที่ไม่สมบูรณ์และไบต์แรกจากไฟล์ที่สอง (เป็นผลรวม 512) ไปยังไฟล์ที่สาม อุปกรณ์ลูปสำหรับไฟล์ที่สองจะต้อง--offsetแล้ว
Hauke ​​Laging

โซลูชั่นที่สง่างาม +1 ยัง Hauke ​​Laging ผู้แนะนำวิธีแก้ไขปัญหาหากขนาดไฟล์แรกไม่ได้เป็นหลายเท่าของ 512
Olivier Dulac

9

คุณจะต้องเขียนสิ่งที่คัดลอกข้อมูลเป็นกลุ่มที่มีขนาดใหญ่ที่สุดเท่ากับจำนวนพื้นที่ว่างที่คุณมี มันควรจะทำงานแบบนี้:

  • อ่านบล็อกของข้อมูลจากfile2(ใช้pread()โดยค้นหาก่อนอ่านไปยังตำแหน่งที่ถูกต้อง)
  • file1ผนวกกับบล็อก
  • ใช้fcntl(F_FREESP)เพื่อ deallocate file2พื้นที่จาก
  • ทำซ้ำ

1
ฉันรู้ว่า ... แต่ฉันไม่สามารถคิดวิธีที่ไม่เกี่ยวข้องกับการเขียนรหัสและฉันคิดว่าการเขียนสิ่งที่ฉันเขียนนั้นดีกว่าที่จะเขียนอะไร ฉันไม่คิดว่าเคล็ดลับอันชาญฉลาดของคุณจะเริ่มจากจุดจบ!
Celada

ของคุณเช่นกันจะไม่ทำงานหากไม่เริ่มจากจุดจบ
Hauke ​​Laging

ไม่มันทำงานได้ตั้งแต่ต้นเพราะfcntl(F_FREESP)พื้นที่ว่างที่เกี่ยวข้องกับช่วงไบต์ที่กำหนดของไฟล์ (ทำให้เบาบาง)
Celada

มันเท่ห์มาก แต่ดูเหมือนจะเป็นคุณสมบัติใหม่มาก มันไม่ได้กล่าวถึงในfcntlหน้าคนของฉัน(2012-04-15)
Hauke ​​Laging

4
@HaukeLaging F_FREESP เป็นโซลาริสตัวหนึ่ง บน Linux (ตั้งแต่ 2.6.38) เป็นธง FALLOC_FL_PUNCH_HOLE ของfallocatesyscall ยูทิลิตี้ fallocate รุ่นที่ใหม่กว่าutil-linuxมีส่วนต่อประสานกับส่วนนั้น
Stéphane Chazelas

0

ฉันรู้ว่ามันเป็นวิธีการแก้ปัญหามากกว่าสิ่งที่คุณขอ แต่มันจะดูแลปัญหาของคุณ (และมีการกระจายตัวเล็กน้อยหรือ headcratch):

#step 1
mount /path/to/... /the/new/fs #mount a new filesystem (from NFS? or an external usb disk?)

แล้ว

#step 2:
cat file* > /the/new/fs/fullfile

หรือถ้าคุณคิดว่าการบีบอัดจะช่วย:

#step 2 (alternate):
cat file* | gzip -c - > /the/new/fs/fullfile.gz

จากนั้น (และจากนั้นเท่านั้น) ในที่สุด

#step 3:
rm file*
mv /the/new/fs/fullfile  .   #of fullfile.gz if you compressed it

น่าเสียดายที่ดิสก์ usb ภายนอกต้องการการเข้าถึงทางกายภาพและ nfs ต้องการฮาร์ดแวร์เพิ่มเติมและฉันไม่มีอะไรเลย อย่างไรก็ตามขอขอบคุณ =)
เร่ง

ฉันคิดว่ามันจะเป็นอย่างนั้น ... คำตอบของ Rob Bos คือสิ่งที่ดูเหมือนว่าตัวเลือกที่ดีที่สุดของคุณ (โดยไม่ต้องเสี่ยงกับการสูญเสียข้อมูลโดยการตัด - ในขณะที่คัดลอกและโดยไม่กระทบข้อ จำกัด ของ FS เช่นกัน)
Olivier Dulac
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.