将大文件相互附加而不复制它们

将大文件相互附加而不复制它们

有 5 个大文件(文件 1、文件 2、.. 文件 5),每个文件大约 10G,磁盘上剩余的可用空间极低,我需要将所有这些文件连接成一个。无需保留原始文件,只需保留最终文件。

cat通常的串联是按文件顺序进行的file2.. file5

cat file2 >> file1 ; rm file2

不幸的是,这种方式需要至少 10G 的可用空间,我没有。有没有办法在不实际复制文件的情况下连接文件,但以某种方式告诉文件系统 file1 不会在原始 file1 结束处结束并在 file2 开始处继续?

附注如果重要的话,文件系统是 ext4。

答案1

AFAIK(不幸的是)不可能从头开始截断文件(这对于标准工具可能是正确的,但对于系统调用级别看这里)。但增加一些复杂性后,您可以使用正常截断(与稀疏文件一起):您可以写入目标文件的末尾,而无需写入其间的所有数据。

首先,我们假设这两个文件的大小正好是 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块大小,生成的文件将是一场碎片噩梦。

答案2

如果您的程序无法处理多个文件,则可以使用命名管道模拟单个文件,而不是将这些文件合并到一个文件中。

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 整除,否则您将丢失一些数据。如果是的话,那你就很好了。我看到他也在下面指出了这一点。

答案3

您必须编写一些东西来复制数据,这些数据最多与您拥有的可用空间量一样大。它应该像这样工作:

  • 从中读取数据块file2pread()在读取到正确位置之前使用通过查找)。
  • 将块附加到file1.
  • 用于fcntl(F_FREESP)取消分配 中的空间file2
  • 重复

答案4

我知道这比您所要求的更多是一种解决方法,但它会解决您的问题(并且几乎没有碎片或麻烦):

#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

相关内容