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

rus*_*ush 45 filesystems files

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

通常的连接是cat按顺序进行的文件file2.. file5

cat file2 >> file1 ; rm file2
Run Code Online (Sandbox Code Playgroud)

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

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

Hau*_*ing 21

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
    
    Run Code Online (Sandbox Code Playgroud)

但是请先尝试使用较小的测试文件,请...

可能文件既不是相同的大小,也不是块大小的倍数。在这种情况下,偏移量的计算变得更加复杂。seek_bytes然后skip_bytes应该使用。

如果这是您想要的方式,但需要有关详细信息的帮助,请再次询问。

警告

根据dd块大小,生成的文件将是碎片化的噩梦。

  • 如果没有稀疏文件支持,那么您可以逐块反转第二个文件,然后删除最后一个块并将其添加到第二个文件 (4认同)

Rob*_*Bos 18

如果您的程序无法处理多个文件,那么与其将文件合并到一个文件中,不如用命名管道模拟单个文件。

mkfifo /tmp/file
cat file* >/tmp/file &
blahblah /tmp/file
rm /tmp/file
Run Code Online (Sandbox Code Playgroud)

正如 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
Run Code Online (Sandbox Code Playgroud)

然后,/dev/dm-0 包含一个以您的文件为内容的虚拟块设备。

我没有很好地测试过这个。

另一个编辑:文件大小必须能被 512 整除,否则你会丢失一些数据。如果是,那么你很好。我看到他在下面也提到了这一点。

  • @rush 更好的选择可能是在每个文件上放置一个循环设备,并通过 `dmsetup` 将它们组合到一个虚拟块设备(它允许正常的查找操作,但既不追加也不截断)。如果第一个文件的大小不是 512 的倍数,那么您应该将不完整的最后一个扇区和第一个字节从第二个文件(总和 512)复制到第三个文件。那么第二个文件的循环设备将需要`--offset`。 (7认同)

Cel*_*ada 10

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

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

  • @HaukeLaging F_FREESP 是 Solaris 之一。在 Linux 上(自 2.6.38 起),它是 `fallocate` 系统调用的 FALLOC_FL_PUNCH_HOLE 标志。来自`util-linux` 的新版本 fallocate 实用程序有一个接口。 (4认同)