Nối các tệp lớn với nhau mà không cần sao chép chúng


41

Có 5 tệp rất lớn (file1, file2, .. file5) mỗi tệp khoảng 10G và dung lượng trống rất thấp còn lại trên đĩa và tôi cần ghép tất cả các tệp này thành một. Không cần phải giữ các tệp gốc, chỉ có tệp cuối cùng.

Kết nối thông thường sẽ diễn ra cattheo trình tự cho các tệp file2.. file5:

cat file2 >> file1 ; rm file2

Thật không may, cách này đòi hỏi ít nhất 10G dung lượng trống mà tôi không có. Có cách nào để nối các tệp mà không cần sao chép thực tế nhưng nói với hệ thống tệp bằng cách nào đó rằng tệp1 không kết thúc ở đầu tệp1 cuối và tiếp tục khi bắt đầu tệp2?

ps. hệ thống tập tin là ext4 nếu vấn đề đó.


2
Tôi rất muốn xem giải pháp, nhưng tôi nghi ngờ rằng điều đó là không thể nếu không trực tiếp làm hỏng hệ thống tập tin.
Kevin

1
Tại sao bạn cần phải có một tệp vật lý lớn như vậy? Tôi đang hỏi bởi vì có lẽ bạn có thể tránh được việc ghép nối mà, như các câu trả lời hiện tại cho thấy, thật khó chịu.
liori

6
@rush: thì câu trả lời này có thể giúp: serverfault.com/a/487692/16081
liori

1
Thay thế cho trình ánh xạ thiết bị, kém hiệu quả hơn, nhưng dễ thực hiện hơn và dẫn đến một thiết bị có thể phân vùng và có thể được sử dụng từ một máy từ xa là sử dụng chế độ "đa" nbd-server.
Stéphane Chazelas

1
Họ luôn gọi tôi là ngu ngốc khi tôi nói rằng tôi nghĩ điều này thật tuyệt.
n611x007

Câu trả lời:


19

AFAIK, thật không may là không thể cắt một tập tin ngay từ đầu (điều này có thể đúng với các công cụ tiêu chuẩn nhưng đối với cấp độ tòa nhà xem ở đây ). Nhưng với việc thêm một số phức tạp, bạn có thể sử dụng cách cắt ngắn thông thường (cùng với các tệp thưa thớt): Bạn có thể ghi vào cuối tệp đích mà không phải ghi tất cả dữ liệu ở giữa.

Giả sử đầu tiên cả hai tệp chính xác là 5GiB (5120 MiB) và bạn muốn di chuyển 100 MiB cùng một lúc. Bạn thực hiện một vòng lặp bao gồm

  1. sao chép một khối từ cuối tệp nguồn vào cuối tệp đích (tăng dung lượng đĩa đã sử dụng)
  2. cắt bớt tệp nguồn theo một khối (giải phóng không gian đĩa)

    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
    

Nhưng hãy thử với các tệp thử nghiệm nhỏ hơn trước, vui lòng ...

Có lẽ các tệp không có cùng kích thước cũng như bội số của kích thước khối. Trong trường hợp đó, việc tính toán bù đắp trở nên phức tạp hơn. seek_bytesskip_bytesnên được sử dụng sau đó.

Nếu đây là con đường bạn muốn đi nhưng cần giúp đỡ để biết chi tiết thì hãy hỏi lại.

Cảnh báo

Tùy thuộc vào ddkích thước khối, tập tin kết quả sẽ là một cơn ác mộng phân mảnh.


Có vẻ như đây là cách dễ nhất để ghép các tệp. Cảm ơn về lời khuyên.
vội vàng

3
nếu không có hỗ trợ tệp thưa thớt thì bạn có thể chặn ngược lại tệp thứ hai tại chỗ và sau đó chỉ cần xóa khối cuối cùng và thêm nó vào tệp thứ hai
ratchet freak

1
Tôi đã không thử bản thân mình (mặc dù tôi sắp sửa), nhưng seann.herdejurgen.com/resume/samag.com/html/v09/i08/a9_l1.htmlm là tập lệnh Perl tuyên bố thực hiện thuật toán này.
zwol

16

Thay vì sắp xếp các tệp lại với nhau thành một tệp, có thể mô phỏng một tệp duy nhất với một ống có tên, nếu chương trình của bạn không thể xử lý nhiều tệp.

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

Như Hauke ​​gợi ý, losetup / dmsetup cũng có thể hoạt động. Một thử nghiệm nhanh chóng; Tôi đã tạo 'file1..file4' và với một chút nỗ lực, đã làm:

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

Sau đó, / dev / dm-0 chứa một thiết bị khối ảo với tệp của bạn dưới dạng nội dung.

Tôi đã không kiểm tra điều này tốt.

Chỉnh sửa khác: Kích thước tệp phải chia đều cho 512 hoặc bạn sẽ mất một số dữ liệu. Nếu có, thì bạn tốt. Tôi thấy ông cũng lưu ý rằng dưới đây.


Thật là một ý tưởng tuyệt vời để đọc tệp này một lần, thật không may, nó không có khả năng nhảy qua fifo lùi / tiến, phải không?
vội vàng

7
@rush Giải pháp thay thế ưu việt có thể là đặt một thiết bị lặp trên mỗi tệp và kết hợp chúng thông qua dmsetupmột thiết bị khối ảo (cho phép các hoạt động tìm kiếm thông thường nhưng không nối thêm hoặc cắt bớt). Nếu kích thước của tệp đầu tiên không phải là bội số của 512 thì bạn nên sao chép cung cuối cùng chưa hoàn thành và các byte đầu tiên từ tệp thứ hai (tổng cộng 512) sang tệp thứ ba. Thiết bị lặp cho tệp thứ hai sẽ cần --offsetsau đó.
Hauke ​​Laging

giải pháp thanh lịch. +1 cũng cho Hauke ​​Laging, người đề xuất cách khắc phục sự cố nếu kích thước của tệp đầu tiên không phải là bội số của 512
Olivier Dulac

9

Bạn sẽ phải viết một cái gì đó sao chép dữ liệu thành từng bó lớn nhất bằng dung lượng trống mà bạn có. Nó sẽ hoạt động như thế này:

  • Đọc một khối dữ liệu từ file2(sử dụng pread()bằng cách tìm kiếm trước khi đọc đến vị trí chính xác).
  • Nối khối vào file1.
  • Sử dụng fcntl(F_FREESP)để phân bổ không gian từ file2.
  • Nói lại

1
Tôi biết ... nhưng tôi không thể nghĩ ra bất kỳ cách nào không liên quan đến việc viết mã, và tôi nghĩ rằng viết những gì tôi viết thì tốt hơn là không viết gì cả. Tôi đã không nghĩ về thủ thuật thông minh của bạn bắt đầu từ cuối!
Celada

Bạn cũng vậy, sẽ không làm việc mà không bắt đầu từ cuối, phải không?
Hauke ​​Laging

Không, nó hoạt động ngay từ đầu vì fcntl(F_FREESP)nó giải phóng không gian được liên kết với một phạm vi byte nhất định của tệp (nó làm cho nó thưa thớt).
Celada

Điều đó thật tuyệt. Nhưng dường như là một tính năng rất mới. Nó không được đề cập trong fcntltrang người đàn ông của tôi (2012-04-15).
Hauke ​​Laging

4
@HaukeLaging F_FREESP là một Solaris. Trên Linux (kể từ 2.6,38), đó là cờ FALLOC_FL_PUNCH_HOLE của tòa nhà fallocate. Các phiên bản mới hơn của tiện ích fallocate từ util-linuxcó một giao diện đến đó.
Stéphane Chazelas

0

Tôi biết đó là một cách giải quyết nhiều hơn những gì bạn yêu cầu, nhưng nó sẽ giải quyết vấn đề của bạn (và với sự phân mảnh nhỏ hoặc khăn trùm đầu):

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

và sau đó

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

hoặc, nếu bạn nghĩ rằng nén sẽ giúp:

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

Sau đó (và CHỈ sau đó), cuối cùng

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

Thật không may, đĩa usb ngoài yêu cầu truy cập vật lý và nfs yêu cầu phần cứng bổ sung và tôi không có gì của nó. Dù sao cũng cảm ơn. =)
vội vàng

Tôi nghĩ rằng nó sẽ như vậy ... Câu trả lời của Rob Bos là lựa chọn tốt nhất của bạn (không có nguy cơ mất dữ liệu bằng cách cắt xén trong khi sao chép và cũng không gặp phải giới hạn của FS)
Olivier Dulac
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.