26

我指的是如何用很少的备用磁盘空间连接两个大文件?

我正在实施以下内容:

  1. 分配组合大小的稀疏文件。
  2. 将第二个文件末尾的 100Mb 复制到新文件的末尾。
  3. 截断第二个文件末尾的 100Mb
  4. 循环 2&3 直到完成第二个文件(将 2. 修改到目标文件中的正确位置)。
  5. 执行 2&3&4 但使用第一个文件。

我想知道是否有人能够在 linux 中“截断”给定文件?截断是按文件大小进行的,例如,如果文件是 10GB,我想截断文件的前 100MB,并留下剩余的 9.9GB。任何人都可以在这方面提供帮助吗?

谢谢

4

9 回答 9

30

回答,现在 Linux 内核 v3.15 (ext4/xfs) 已成为现实

在这里阅读 http://man7.org/linux/man-pages/man2/fallocate.2.html

测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>

#ifndef FALLOC_FL_COLLAPSE_RANGE
#define FALLOC_FL_COLLAPSE_RANGE        0x08
#endif

int main(int argc, const char * argv[])
{
    int ret;
    char * page = malloc(4096);
    int fd = open("test.txt", O_CREAT | O_TRUNC | O_RDWR, 0644);

    if (fd == -1) {
        free(page);
        return (-1);
    }

    // Page A
    printf("Write page A\n");
    memset(page, 'A', 4096);
    write(fd, page, 4096);

    // Page B
    printf("Write page B\n");
    memset(page, 'B', 4096);
    write(fd, page, 4096);

    // Remove page A
    ret = fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, 0, 4096);
    printf("Page A should be removed, ret = %d\n", ret);

    close(fd);
    free(page);

    return (0);
}
于 2014-08-01T10:43:31.990 回答
21

对于大多数文件系统来说,切断文件的开头是不可能的,并且没有通用的 API 来做到这一点;例如 truncate 函数只修改文件的结尾。

不过,您也许可以使用某些文件系统来做到这一点。例如,ext4 文件系统最近获得了一个 ioctl,您可能会发现它很有用:http: //lwn.net/Articles/556136/


更新:fallocate在写完这个答案大约一年后,通过模式添加了对从 ext4 和 xfs 文件系统上文件的开头和中间删除块的支持FALLOC_FL_COLLAPSE_RANGE。这比自己使用低级iotcl更方便。

还有一个与 C 函数同名的命令行实用程序。假设您的文件位于受支持的文件系统上,这将删除前 100MB:

fallocate -c -o 0 -l 100M yourfile

删除前 1GB:

fallocate -c -o 0 -l 1G yourfile
于 2013-08-06T05:51:02.083 回答
4

请阅读一本好的 Linux 编程书籍,例如Advanced Linux Programming

您需要使用Linux 内核 syscalls,请参阅syscalls(2)

特别是truncate(2)(既用于截断,也用于在支持它的文件系统上扩展稀疏文件)和stat(2)以显着获取文件大小。

没有(便携式或文件系统中立)方法可以从文件的开头(或中间)删除字节,您只能在文件末尾截断文件。

于 2013-08-06T05:20:19.700 回答
2

相关:在最大磁盘空间为 1 TB 的系统上,如何从 700 GB txt 文件中删除前 3 亿行?在 unix.SE 上指出,您可以dd在适当的位置 (conv=notrunc) 在截断之前在文件中更早地复制数据,无需额外的磁盘空间即可完成工作。

作为将数据从一个文件的开头转移到另一个文件的结尾的重复过程的一部分,这太可怕了。但值得一提的是其他用例,其中截断前面的目的实际上是将文件中的特定点放在前面,而不仅仅是释放磁盘空间。


我想截断文件的前 100MB 并留下剩余的 9.9GB

这与步骤列表所说的相反,来自如何用很少的备用磁盘空间连接两个大文件的答案?你说你正在关注。@Douglas Leeder 建议复制到稀疏文件的中间,因此您只需要在末尾截断,这很容易且可通过ftruncate(2)您用于读取该文件的 open fd 上的 POSIX 系统调用进行移植。


但是,如果您想避免复制第一个文件,而只是将第二个文件附加到第一个文件的末尾,是的,您确实需要在阅读第二个文件的开头释放数据。 但请注意,您不需要完全截断它。您只需要释放该空间,例如通过使现有文件稀疏替换分配的空间为“洞”。

Linux 特定的系统调用fallocate(2)可以FALLOC_FL_PUNCH_HOLE在 FS 上执行此操作,包括 XFS(自 Linux 2.6.38 起)、ext4(自 3.0 起)、BTRFS(自 3.7 起)。

所以它比FALLOC_FL_COLLAPSE_RANGE(Linux 3.15)更早可用,它缩短了文件而不是留下一个洞。Linux 3.15 现在已经很老了,所以希望这无关紧要。

在您读取数据(并将其安全地写入另一个文件)之后在数据中打孔可能比在文件中移动数据更简单,因为可以确定您正在读取的文件描述符的文件位置的语义,如果它在您使用时打开FALLOC_FL_COLLAPSE_RANGE

fallocate(1)命令行工具是围绕该系统调用构建的,允许您在支持它们的系统上执行这些操作。

于 2020-09-22T02:47:39.770 回答
2

我发现我必须在文件缩小之前使用fallocate和的组合sed,所以我有一个 43MB 的文件,我想把它缩小到 5MB 左右

fallocate -p -o 0 -l 38m fallocate.log

我注意到这用一堆“废话”字符填充了第一行,但我的文件大小仍然是 43MB

然后我用来sed删除第一行

sed -i 1d fallocate.log

文件大小现在为 4.2MB。

于 2020-05-09T00:51:01.397 回答
2

如果您可以使用 ASCII 行而不是字节,那么删除文件的前 n 行很容易。例如删除前 100 行:

sed -i 1,100d /path/to/file
于 2016-04-26T15:26:10.913 回答
1

到目前为止,这是一个相当老的问题,但这是我的看法。排除在可用空间有限的情况下完成的要求,我将使用类似于以下内容的内容来截断文件的前 100mb:

$ tail --bytes=$(expr $(wc -c < logfile.log) - 104857600) logfile.log > logfile.log.tmp
$ mv logfile.log.tmp logfile.log

解释:

  • 这将输出文件的最后 nn 个字节(tail --bytes)。
  • 文件中要输出的字节数计算为文件大小 (wc -c < logfile.log) 减去 100Mb (expr $( ... ) - 104857600)。这将使我们的文件大小比文件大小少 100Mb(例如 9.9Gb)
  • 然后将其输出到临时文件,然后移回原始文件名以保留截断的文件。
于 2016-11-30T02:19:26.017 回答
1

从文件中删除除最后 10,000 行之外的所有行。

sed -i 1,$( ( $(wc -l < path/to/file) -10000 ) )d path/to/file 
于 2017-03-28T18:04:47.217 回答
0

选项1 - cut -b SIZE_TO_TRUNCATE_KB- <file_name>

选项 2——echo "$(tail -<NO_OF_LINES> <file_name>)" > <file_name>

于 2021-06-27T11:00:33.107 回答