从1TB磁盘上的700 GB文件中删除前300M行

2020-09-22 20:55:39

但是我认为(请纠正我)我不能使用它们,因为磁盘空间被限制在1TB,并且它们在处理过程中产生一个新文件和/或有一个临时文件。

如果您可以压缩文件,并且压缩后留下足够的空间来存储压缩文件的第二个副本,那么这将是可行的方法。解决您的问题的管道将通过某种转换流式传输解压缩的数据,然后压缩处理后的数据。例如,参见Terdon的答案。 -Kusalananda♦。

参见截断Linux on Stack Overflow中文件的前100MB以获得一个非常有趣的选项,即就地截断文件的前面。注意,我还没有试过,强烈建议您先在一次性文件上测试它 --罗伊马。

这是真实的情况还是假设的情况?我想不出一个现实的情况下,700 GB的文本会包含一些有意义的内容。找背景资料。 -约翰·克里吉(Criggie)。

你有备份,对吗?将其视为源文件并覆盖目标文件。如果您没有备份,那么很明显,数据并不重要,所以您是否删除它并重新开始也无关紧要 --罗伊马。

可以使用DD(或者可替换地使用环路设备)就地完成移除前n行(或字节)。没有临时文件,也没有大小限制;但是,这很危险,因为没有进度跟踪,任何错误都会给您留下一个损坏的文件。

$stat+%c 1000lines.txt3893#总字节数$head-n 300 1000lines.txt|wc-b1092#前300行字节$ECHO$((3893-1092))2801#删除后的目标文件大小。

所以文件是3893字节,我们想要去掉前1092字节,剩下一个新的2801字节的文件。

GNU dd命令使用conv=notrunc,否则文件将在复制其内容之前被删除:

$dd conv=notrunc iflag=SKIP_BYTES SKIP=1092 if=1000lines.txt of=1000lines.txt5+1条记录,共复制2801字节(2.8 kB,2.7 KiB),8.6078e-05 s,32.5 MB/s。

这删除了前300行,但现在重复最后1092个字节,因为文件尚未被截断:

这会将文件缩小到新的大小,删除文件末尾的重复行。

对于较大的文件,该过程的工作方式是相同的。只需设置较大的块大小即可获得更好的性能。

主要问题是确定准确行号的正确字节偏移量。基本上,只有通过阅读和计数才能做到这一点。因此,即使您要丢弃其中的一大部分,您仍然必须至少读取整个文件一次。

如果我的生活依赖于此解决方案,我就不会使用它;-)就地编辑文件可能会导致各种问题,包括完全丢失其内容。 --阿特姆·S·塔什基诺夫。

@ArtemS.Tashkinov当然...。原地作业总是危险的。这甚至适用于非破坏性坏块、LVM pvmove、mdadm Growth等。无论如何,对于那些特别担心dd的人,我还在另一个答案中添加了循环设备方法。 -弗罗斯特·舒茨(Frostschutz)

如果=1000lines.txt of=1000lines.txt在生成输出时没有创建相当大的临时文件,那么如何知道dd conv=notrunc iflag=SKIP_BYTES SKIP=1092(使得此解决方案不适用于OP)? -dchux-恢复莫妮卡。

DD不会创建临时文件。此答案假设您的文件系统行为正常。如果您将网络或融合文件系统添加到混合环境中,则可能不会以这种方式工作。 -弗罗斯特·舒茨(Frostschutz)。

这将首先对原始输入文件(File)进行gzip以创建file.gz。然后,zcat新创建的文件e.gz,通过Tail-n+300000001通过管道传输它以删除前3M行,压缩结果以节省磁盘空间并将其另存为newFile.gz。&;&;确保只有在gzip操作成功时才继续(如果空间用完,它将失败)。

请注意,文本文件是非常可压缩的。例如,我使用seq 400000000&>file创建了一个测试文件,该文件打印从1到400,000,000之间的数字,结果是一个3.7G的文件。当我使用上面的命令进行压缩时,压缩文件只有849M,我创建的newFile.gz只有213M。

Seq产生的文本的熵可能非常低,这就是为什么这个特定的文本非常可压缩的原因。一些更随机的东西很可能会表现得更糟糕。 -很可能是某人。

我猜700 GB的文本文件(带行)的内容也不是很随机。操作没有指定,因为我猜它包含类似日志记录数据或数据库转储之类的内容。它很可能会降到原来大小的25%以下。如果一切都是7位ASCII字符,那么已经有大约50%的减少空间了。 -Avee。

OP指出磁盘上有340 GB的可用空间,因此可能不需要第一个gzip,因为压缩后得到的较小文件可能适合。那可能会让它更快一些。 -Avee。

@可能_某人是的,这是一个公平的观点。这是最简单的演示方式。 --Tterdon♦

@Avee Tail可以生成临时文件,因此它在运行时需要的空间比最终文件占用的空间多。在任何情况下,将700 GB的文本文件解压缩都是浪费空间,所以您不妨不管怎样压缩它。 --Tterdon♦。

您可以使用losetup来完成此操作,作为这里描述的dd方法的替代方法。再说一次,这种方法仍然是危险的。

$seq 1 1000>;1000lines.txt$stat+%c 1000lines.txt3893#总字节数$head-n 300 1000lines.txt|wc-b1092#前300行字节$ECHO$((3893-1092))2801#删除后的目标文件大小。

#losetup--find--show 1000lines.txt/dev/loop0losetup:1000lines.txt:\警告:文件不适合512字节的扇区;\文件结尾将被忽略。#head-n 3/dev/loop01 2 3#ail-n 3/dev/loop0921922923。

循环设备要求其备份文件是扇区大小的倍数。带行文本文件通常不符合该方案,因此,为了不错过文件末尾(最后一个部分扇区)内容,只需先追加一些数据,然后重试:

#head-c 512/dev/zero>;>;1000lines.txt#losetup--find--show 1000lines.txt/dev/loop1losetup:1000lines.txt:\警告:文件不适合512字节的扇区;\文件结尾将被忽略。#ail-n 3/dev/loop19991000\0。

#losetup--find--show--offset=1092 1000lines.txt/dev/loop2losetup:1000lines.txt:\警告:文件不适合512字节的扇区;\文件结尾将被忽略。#head-n 3/dev/loop2301302303#ail-n 3/dev/loop29991000\0。

下面是环路设备的优点。您不必担心意外截断文件。在执行任何操作之前,您还可以轻松地验证您的偏移量是否确实正确。

如果您在Linux上,并且文件系统是ext4或XFS,并且此移动的目的是释放文件系统上的空间(否则,您可以保持文件原样,在读取文件时只查找它),则可以使用Fallocate系统调用或命令行实用程序的折叠范围功能。

唯一的问题是它只能折叠完整的块,所以您可能会在文件的开头留下一个部分行和一些不需要的额外行。

#truncate-s 1Gext4.img#mkdir dir#mkfs.ext4ext4.imgmke2fs 1.44.5(2018年12月15日)...#mount ext4.img dir#seq 100000000>;dir/file.txt#du-sh dir/file.txt#head-n 30000000 dir/file.txt|wc-c258888897#falocate-c-l";$(258888897/4096*4096)";Dir/file.txt#du-sh dir/file.txt601M dir/file.txt#head-n 3 dir/file.txt52999986629999867#sed';/30000000/q';dir/file.txt|wc-c1217#读取文件时应查找的额外字节。

很好,这就是我要建议的。如果您想要保留的行不是从块边界开始的,那么首先执行此操作对于压缩和解压缩可能仍然是一个有用的设置。虽然就地复制dd可能是最好的,而且不需要任何额外的空间,因此根本不会从中受益。 -彼得·科德斯(Peter Cordes)。

所有就地工作的方法都是有风险的。如果您重视您的数据,请购买另一张磁盘。

如果无法将新磁盘连接到通常的磁盘接口(或不欢迎重新启动),有几种方法可以将磁盘连接到USB端口,这些方法在正常情况下会使磁盘在插入后可以访问。或者直接购买U盘,您就有了一个方便的备份设备以备将来使用。 -Pavel。

+1:操作员告诉我们这是关于一家公司的数据库。这让我同意购买磁盘作为临时存储是最好的解决方案。(阅读有关在可用硬件中解决问题的或多或少的高级方法是很有趣的,但不是最好的解决方案。) --苏多德(Sudodus)。

此问题的限制是无论它位于何处,存储容量都是有限的。不需要很大的RAM,因为从根本上说,您只需从存储文件的位置读取一个字节,然后将该字节[字符]写出或不写出到新文件(无论该文件位于何处)。内部文件和外部文件可以位于完全不同的地方。在单独的分区、磁盘上或跨网络。您不需要读取和写入同一文件夹。因此,对于附加的程序,您只需给出和的完整路径名,即可解决磁盘空间限制问题。您将受到其他限制的支配,例如磁盘或网络I/O速度,但它会起作用。花很长时间上班总比不能上班要好。

调整LL,这是我过去一次从文本文件中读取整行的硬编码行长,我将其设置为2048个字符。如果愿意,可以将其设置为1000000,如果文本文件中有非常长的行,则需要1MB内存。

如果你的文本文件大得离谱...。我经常处理高达10 GB的文本文件...。考虑对其执行gzip-9以创建mytextfile.gz。作为文本文件可能会压缩到5%的大小,考虑到磁盘I/O速度与CPU速度相比,这是很有帮助的。

我将您的新文件(带有nDELETED_LINES)写出到一个未压缩的文本文件中,所以这可能会很大。

您不必压缩原始文本文件即可执行此操作,压缩它是可选的。

您可以将原始文件放在一个磁盘或网络位置上,然后将删除了N行的输出文件写入其他磁盘或网络位置,例如,只需使用完整的命名约定。

/*这个由GCC编译的名为delete_n_lines.c的文件-W delete_n_lines.c-o delete_n_lines.x-lz已经通过";gzip-9";压缩了您的大型文本文件。为节省磁盘空间,此程序还将读取常规的未压缩文本文件*/#include<;stdlib.h>;#include<;stdio.h>;#include<;string.h>;#include<;zlib.h>;#定义LL2048/*行长,字符数最多为';\n';*/int main(int argc,char*argv[]){gzFile fin;file*fout;char line[LL];long int i,n=0;long int n_line_to_delete=0;if(argc!=4){printf(";用法:%s<;infile>;<;outfile>;<;first_N_line_to_delete>;\n\n";,argv[0]);退出(0);}n=sscanf(argv[3],";%d";,&;n_line_to_delete);if(n==0){printf(";\n错误:读取N行以删除时出现问题\n\n";);exit(0);}if(strcmp(argv[1],argv[2])==0){printf(";;\n错误:内部文件和输出文件相同。\n";);printf(";);printf(";)。不执行\n\n&34;);退出(0);}fout=fopen(argv[2],";w";);如果(fout==null){printf(";\n错误:无法写入%s\n\n";,argv[2]);退出(0);}fin=gzopen(argv[1],";r";);if(fin=null){printf(";\n错误:无法读取%s\n\n";,argv[1]);fclose(Fout);exit(0);}n=0;gzgets(fin,line,ll);而(!Gzeof(Fin)){if(n<;n_line_to_delete)n++;Else fput(line,fout);gzgets(fin,line,LL);}gzclose(Fin);fclose(Fout);printf(";\n删除%s的前%d行,输出文件为%s\n\n";,n,argv[1],argv[2]);返回0;}。

仅仅是实际识别一个700 GB的文本文件,这真的*大,在1TB的磁盘上,如果在尝试压缩一个小时后对其执行gzip操作失败,我也不会感到惊讶。因此,需要700 GB来保存该文本文件,并假设>;500 GB来保存只删除了300万行的结果输出文件,那么您将不得不在其他地方寻找存储空间。除了[购买]另一个磁盘之外,您可以使用一些承载N TB的在线存储,然后以任何可以接受的方式将其挂载到Linux中。 --罗恩。

Gzip压缩它不会有问题,唯一的问题是是否有足够的空间来写入压缩的输出。 -安赫尔

如果你真的需要这个任务,再投一票给定制程序。C或任何功能强大的动态语言(如Perl或Python)都可以。我不会在这里写出来源,但会描述在您四处移动数据时防止数据丢失的算法:

从末尾计数换行符开始读你的大文件。在收集了一些您可以安全地放入空闲空间的预定义数量的行之后,将此块作为单独的文件写入,并剪切大文件的尾部。使用块的文件名存储行号。

在此之后,您将以完全擦除的大文件和占用相同空间的许多小得多的文件结束。

清点你的3亿行--你可以立即删除对应于不必要行的所有块,因为你知道哪些块包含哪些行。

如果您实际上不需要这个大文件,您可以使用任何您需要的工具直接操作剩余的块,使用通配符或根据需要使用cat将它们串在一起。

如果您毕竟需要大文件,并且在删除不必要的文件之后,释放的空间足以存储剩余块的总和-只需将它们与cp或cat组合在一起即可。

如果您需要大文件,但没有足够的空间,请编写另一个小程序,它将执行与步骤1相反的操作:将列表和每个块的单独长度保存到某个列表文件中。逐个阅读数据块并将其附加到新创建的大文件中。每次将块附加到大文件后,您将删除一个包含此块的单独的小文件,从而允许您就地重组文件。如果您在任何时候中断了写入块的过程,您可以通过计算任何特定块的正确偏移量来重新开始写入大文件,因为您已经预先保存了每个块的大小。

1<;>;运算符是标准1<;>;运算符(在不截断的情况下以读+写模式打开)上特定于ksh93的变体,如果该命令成功,则该运算符在命令返回到该命令离开其标准输出的位置后会截断文件。

对于其他shell,您始终可以手动执行就地截断,例如使用Perl:

{Tail-n+300000001;&;&;perl-e&e#39;截断标准,告知标准1文件(<;&>)。

{head-n 300000000|pv-s 300000000-Ln&39;跳过300M行;/dev/null&;&;cat|pv-N&39;重写其余的&39;&;perl-e&e;截断标准,告诉标准文件1<;文件(<;file 1<;<;

请注意,这些是危险的,因为文件正被就地覆盖。如果前300M行包含空洞(对于有效的文本文件不应该发生这种情况),并且文件的其余部分占用的空间超过文件系统上的空闲空间,则可能会用完磁盘空间。

我创建了一个可能对您有用的工具:hexpexek是一个十六进制编辑器,专为处理大型文件而设计,可以在任何最新的类似POSIX的系统上运行(在Debian、CentOS和FreeBSD上进行了测试)。

您可以使用hexpoek或外部工具来查找第3亿个换行符。然后,假设X是第300个新行之后的第一个八位字节的十六进制零索引位置,则可以在hexpek中打开该文件,并且单个命令0,xk将删除文件中的前X个八位字节。

Hexexek不需要tmpfile来执行此操作;尽管可选的备份模式确实需要并且很可能需要通过-backup标志禁用(遗憾的是,当前的备份算法不支持影响比备份文件可用的文件空间更多的文件空间的重新安排)。

其他答案是好的,但如果你做错了什么,就会有风险。文件偏移代数容易犯愚蠢的错误(不要问我从哪里知道的)。

我会试一试。文件是文本,所以它的压缩效果可能比3倍好,根据内容的不同,最高可压缩50倍,就像大多数日志文件一样。

Bzip2 file.txt#要使用的文件bzip2-d<;file.txt.zip|sed,grep,ail,任何|bzip2>;file.changed.txt.bz2#动态解压缩,更改,压缩。

您也可以使用语法完全相同的gzip、lzip或xz,这取决于可用的内容和您想要交换的速度与压缩。

现在,您已经将原始文件和更改后的文件都压缩了。如果文件足够压缩,您可以解压缩原始文件或更改后的文件。

Vim将制作原始文件的备份副本,因此磁盘上需要700G的空闲空间,而操作员没有这样的空间。 -斯特凡内·查泽拉斯(Stéphane Chazelas)。

{从文件末尾读取相当数量的块,将块追加到新文件中,将旧文件截断那么多块}当您到达第3,000和1行的开头时是否停止。

现在您应该有一个文件,该文件只包含您想要的行,但没有按顺序排列。

{从新文件末尾读取相同数量的块,将这些块追加到原始文件,将新文件截断那么多块}在处理完整个文件后停止吗?

点击“发布您的答案”,即表示您同意我们的服务条款、隐私政策和Cookie政策。

不是你想要的答案吗?浏览标记的其他问题或提出您自己的问题。