Tambahkan file besar satu sama lain tanpa menyalinnya

41

Ada 5 file besar (file1, file2, .. file5) masing-masing sekitar 10G dan ruang kosong yang sangat rendah tersisa di disk dan saya harus menggabungkan semua file ini menjadi satu. Tidak perlu menyimpan file asli, hanya yang terakhir.

Rangkaian biasa berjalan dengan catberurutan untuk file file2.. file5:

cat file2 >> file1 ; rm file2

Sayangnya cara ini membutuhkan setidaknya 10G ruang kosong yang tidak saya miliki. Apakah ada cara untuk menggabungkan file tanpa menyalinnya tetapi memberitahu filesystem entah bagaimana file1 itu tidak berakhir pada akhir file1 asli dan berlanjut pada mulai file2?

ps. filesystem ext4 jika itu penting.

buru-buru
sumber
2
Saya tertarik untuk melihat solusinya, tetapi saya curiga tidak mungkin tanpa mengacaukan sistem file secara langsung.
Kevin
1
Mengapa Anda perlu memiliki satu file fisik yang sangat besar? Saya bertanya karena mungkin Anda dapat menghindari penggabungan — yang, seperti yang ditunjukkan oleh jawaban saat ini, cukup menyusahkan.
liori
6
@rush: maka jawaban ini mungkin membantu: serverfault.com/a/487692/16081
liori
1
Alternatif untuk device-mapper, kurang efisien, tetapi lebih mudah diimplementasikan dan menghasilkan perangkat yang dapat dipartisi dan dapat digunakan dari mesin jarak jauh adalah dengan menggunakan mode "multi" dari nbd-server.
Stéphane Chazelas
1
Mereka selalu menyebut saya bodoh ketika saya mengatakan bahwa saya pikir ini seharusnya keren.
n611x007

Jawaban:

19

AFAIK (sayangnya) tidak mungkin untuk memotong file dari awal (ini mungkin benar untuk alat standar tetapi untuk tingkat syscall lihat di sini ). Tetapi dengan menambahkan beberapa kerumitan Anda dapat menggunakan pemotongan normal (bersama dengan file jarang): Anda dapat menulis ke akhir file target tanpa harus menulis semua data di antaranya.

Mari kita asumsikan pertama kedua file tersebut tepat 5GiB (5120 MiB) dan Anda ingin memindahkan 100 MiB pada suatu waktu. Anda menjalankan loop yang terdiri dari

  1. menyalin satu blok dari ujung file sumber ke akhir file target (meningkatkan ruang disk yang dikonsumsi)
  2. memotong file sumber dengan satu blok (membebaskan ruang disk)

    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
    

Tapi cobalah dengan file tes yang lebih kecil dulu, tolong ...

Mungkin file-file tersebut tidak memiliki ukuran yang sama atau kelipatan dari ukuran blok. Dalam hal ini perhitungan offset menjadi lebih rumit. seek_bytesdan skip_bytesharus digunakan kemudian.

Jika ini cara yang ingin Anda tuju tetapi butuh bantuan untuk perinciannya maka tanyakan lagi.

Peringatan

Tergantung pada ddukuran blok file yang dihasilkan akan menjadi mimpi buruk fragmentasi.

Hauke ​​Laging
sumber
Sepertinya ini adalah cara yang paling dapat diterima untuk menggabungkan file. Terima kasih atas sarannya.
buru
3
jika tidak ada dukungan file yang jarang, maka Anda dapat memblok dengan bijaksana membalikkan file kedua di tempat dan kemudian cukup menghapus blok terakhir dan menambahkannya ke file kedua
ratchet freak
1
Saya belum mencoba ini sendiri (walaupun saya akan melakukannya), tetapi seann.herdejurgen.com/resume/samag.com/html/v09/i08/a9_l1.htm adalah skrip Perl yang mengklaim untuk mengimplementasikan algoritma ini.
zwol
16

Alih-alih memecah file bersama menjadi satu file, mungkin mensimulasikan file tunggal dengan pipa bernama, jika program Anda tidak dapat menangani banyak file.

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

Seperti yang Hauke ​​sarankan, losetup / dmsetup juga bisa berfungsi. Eksperimen cepat; Saya membuat 'file1..file4' dan dengan sedikit usaha, apakah:

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

Kemudian, / dev / dm-0 berisi perangkat blok virtual dengan file Anda sebagai konten.

Saya belum menguji ini dengan baik.

Suntingan lain: Ukuran file harus dapat dibagi rata oleh 512 atau Anda akan kehilangan beberapa data. Jika ya, berarti Anda baik-baik saja. Saya melihat dia juga mencatat bahwa di bawah ini.

Rob Bos
sumber
Merupakan ide bagus untuk membaca file ini sekali, sayangnya ia tidak memiliki kemampuan untuk melompati mundur / maju, bukan?
buru
7
@ terburu-buru Alternatif unggul mungkin untuk meletakkan perangkat loop pada setiap file dan menggabungkannya dmsetupke perangkat blok virtual (yang memungkinkan operasi pencarian normal tetapi tidak menambahkan atau memotong). Jika ukuran file pertama bukan kelipatan 512 maka Anda harus menyalin sektor terakhir yang tidak lengkap dan byte pertama dari file kedua (dalam jumlah 512) ke file ketiga. Perangkat loop untuk file kedua akan dibutuhkan --offset.
Hauke ​​Laging
solusi elegan. +1 juga untuk Hauke ​​Laging yang menyarankan cara untuk menyelesaikan masalah jika ukuran file pertama bukan kelipatan 512
Olivier Dulac
9

Anda harus menulis sesuatu yang menyalin data dalam tandan yang paling besar sebesar jumlah ruang kosong yang Anda miliki. Seharusnya berfungsi seperti ini:

  • Baca blok data dari file2(menggunakan pread()dengan mencari sebelum membaca ke lokasi yang benar).
  • Tambahkan blok ke file1.
  • Gunakan fcntl(F_FREESP)untuk membatalkan alokasi ruang dari file2.
  • Ulangi
Celada
sumber
1
Saya tahu ... tetapi saya tidak bisa memikirkan cara apa pun yang tidak melibatkan penulisan kode, dan saya pikir menulis apa yang saya tulis lebih baik daripada tidak menulis apa pun. Saya tidak memikirkan trik cerdik Anda mulai dari akhir!
Celada
Anda juga tidak akan bekerja tanpa memulai dari akhir, bukan?
Hauke ​​Laging
Tidak, ini berfungsi dari awal karena fcntl(F_FREESP)yang membebaskan ruang yang terkait dengan rentang byte file yang diberikan (itu membuatnya jarang).
Celada
Itu keren sekali. Tetapi tampaknya menjadi fitur yang sangat baru. Itu tidak disebutkan di fcntlhalaman manual saya (2012-04-15).
Hauke ​​Laging
4
@HaukeLaging F_FREESP adalah Solaris. Di Linux (sejak 2.6.38), ini adalah flag FALLOC_FL_PUNCH_HOLE dari fallocatesyscall. Versi baru dari fallocate utility util-linuxmemiliki antarmuka untuk itu.
Stéphane Chazelas
0

Saya tahu ini lebih merupakan solusi daripada apa yang Anda minta, tetapi itu akan mengatasi masalah Anda (dan dengan sedikit fragmentasi atau jilbab):

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

lalu

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

atau, jika Anda berpikir kompresi akan membantu:

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

Lalu (dan HANYA kemudian), akhirnya

#step 3:
rm file*
mv /the/new/fs/fullfile  .   #of fullfile.gz if you compressed it
Olivier Dulac
sumber
Sayangnya disk usb eksternal memerlukan akses fisik dan nfs memerlukan perangkat keras tambahan dan saya tidak memilikinya. Pokoknya terima kasih. =)
buru
Saya pikir akan seperti itu ... Jawaban Rob Bos adalah apa yang tampaknya pilihan terbaik Anda (tanpa risiko kehilangan data dengan memotong-sambil-menyalin, dan tanpa memukul keterbatasan FS juga)
Olivier Dulac