【问题标题】:Truncating the first 100MB of a file in linux在linux中截断文件的前100MB
【发布时间】:2013-08-06 23:42:06
【问题描述】:

我指的是How can you concatenate two huge files with very little spare disk space?

我正在执行以下操作:

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

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

谢谢

【问题讨论】:

标签: linux file merge truncate


【解决方案1】:

回答,现在 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);
}

【讨论】:

  • GNU_SOURCE_ 需要在包含 fcntl.h 之前定义——至少在 Ubuntu 16.04 上是这样。只有这样 fallocateFALLOC_FL_COLLAPSE_RANGE 才能作为 gnu 特定的(实验性)功能使用。
  • 太棒了!但是我如何从 bash 中使用?假设我不想编译c代码
  • 在shell中你可以使用man 1 fallocate。像这样:fallocate -c -o offset -l length filename。你需要apt install util-linux(从 Ubuntu 18.04 开始)。
【解决方案2】:

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

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


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

还有一个与 C 函数同名的command line utility。假设您的文件位于受支持的文件系统上,这将删除前 100MB:

fallocate -c -o 0 -l 100M yourfile

删除前 1GB:

fallocate -c -o 0 -l 1G yourfile

【讨论】:

  • OP 在问题正文中提到截断 在文件末尾
  • 是的,也是开始。
  • 虽然没有明确的解决方案,但我现在的想法就是利用truncate命令手动截断文件尾部,将文件大小减去100MB。不过感谢您的建议...
  • 请注意,ext3 不支持它并且fallocate: fallocate failed: Operation not supported 出现错误
【解决方案3】:

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

你需要使用Linux kernelsyscalls,见syscalls(2)

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

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

【讨论】:

  • 是的,这正是我的问题所在。无论如何,据我所知,在linux中截断只截断到固定的文件大小。例如,如果您希望文件大小为 4KB,则只需执行truncate -s 4k filename.txt。我想要的是让我的文件的头部或尾部减少 100MB。这可以实现吗?
【解决方案4】:

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

sed -i 1,100d /path/to/file

【讨论】:

  • 行与大小不同。
【解决方案5】:

我发现我必须同时使用fallocatesed 才能缩小文件大小,所以我有一个 43MB 的文件,我想把它缩小到 5MB 左右

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

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

然后我用sed删除了第一行

sed -i 1d fallocate.log

文件大小现在为 4.2MB。

【讨论】:

    【解决方案6】:

    相关:unix.SE 上的How do I remove the first 300 million lines from a 700 GB txt file on a system with 1 TB max disk space? 指出您可以在适当的位置(conv=notrunc)dd 在截断之前复制文件中的数据,无需额外的磁盘空间即可完成工作。

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


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

    这与您说您正在关注的How can you concatenate two huge files with very little spare disk space? 上的答案中的步骤列表所说的相反。 @Douglas Leeder 建议复制到稀疏文件的中间,因此您只需要在 end 处截断,使用您正在使用的 open fd 上的 POSIX ftruncate(2) 系统调用,这很容易且可移植读取该文件。


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

    Linux 特定的系统调用 fallocate(2) 可以在 FS 上使用 FALLOC_FL_PUNCH_HOLE 执行此操作,包括 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) 命令行工具是围绕该系统调用构建的,允许您在支持它们的系统上执行这些操作。

    【讨论】:

      【解决方案7】:

      到目前为止,这是一个相当老的问题,但这是我的看法。排除在可用空间有限的情况下完成的要求,我将使用类似于以下内容的内容来截断文件的前 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
      • 然后将其输出到临时文件,然后移回原始文件名以保留截断的文件。

      【讨论】:

        【解决方案8】:

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

        sed -i 1,$( ( $(wc -l < path/to/file) -10000 ) )d path/to/file 
        

        【讨论】:

        • 问题是基于文件大小,而不是行数
        • 试过这个表达方式,抱歉没用! sed -i 1,$( ( $(wc -l
        • 这对我有用:sed -i 1,$(($(wc -l
        【解决方案9】:

        选项 1 -- cut -b SIZE_TO_TRUNCATE_KB- &lt;file_name&gt;

        选项 2 -- echo "$(tail -&lt;NO_OF_LINES&gt; &lt;file_name&gt;)" &gt; &lt;file_name&gt;

        【讨论】:

          猜你喜欢
          • 2010-10-16
          • 1970-01-01
          • 2010-11-02
          • 1970-01-01
          • 2011-07-03
          • 2012-09-08
          • 2011-08-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多