如何从总磁盘空间为 1 TB、可用空间为 300 GB 的系统上的 700 GB 文本文件中删除前 3 亿行? (我的系统有 2 GB 内存。)我找到的答案使用 sed、tail、head:
但我认为(请纠正我)我无法使用它们,因为磁盘空间限制为 1 TB,并且它们在处理过程中会生成一个新文件和/或有一个 tmp 文件。
该文件包含 JSON 格式的数据库记录。
答案1
删除前 n 行(或字节)可以使用dd
(或者使用循环设备)。它不使用临时文件并且没有大小限制;然而,这是危险的,因为没有进度跟踪,任何错误都会导致文件损坏。
例子:创建一个1000行的示例文件:
$ seq 1 1000 > 1000lines.txt
$ head -n 3 1000lines.txt
1
2
3
$ tail -n 3 1000lines.txt
998
999
1000
我们想要删除前 300 行。对应多少字节?
$ stat -c %s 1000lines.txt
3893 # total bytes
$ head -n 300 1000lines.txt | wc -c
1092 # first 300 lines bytes
$ echo $((3893-1092))
2801 # target filesize after removal
该文件有 3893 字节,我们要删除前 1092 字节,留下一个 2801 字节的新文件。
要删除这些字节,我们使用 GNUdd
命令,conv=notrunc
否则文件将在复制其内容之前被删除:
$ dd conv=notrunc iflag=skip_bytes skip=1092 if=1000lines.txt of=1000lines.txt
5+1 records in
5+1 records out
2801 bytes (2.8 kB, 2.7 KiB) copied, 8.6078e-05 s, 32.5 MB/s
这会删除前 300 行,但现在会重复最后 1092 字节,因为文件尚未被截断:
$ truncate -s 2801 1000lines.txt
这会将文件缩小到最终大小,并删除文件末尾的重复行。
结果:
$ stat -c %s 1000lines.txt
2801
$ head -n 3 1000lines.txt
301
302
303
$ tail -n 3 1000lines.txt
998
999
1000
对于较大文件的过程类似。您可能需要设置更大的块大小以获得更好的性能(块大小选项dd
是bs
)。
主要问题是确定确切行号的正确字节偏移量。一般来说只能通过读和数来完成。使用这种方法,即使您要丢弃其中的很大一部分,也必须至少读取整个文件一次。
答案2
如果您有足够的空间来压缩文件,这应该会释放大量空间,允许您执行其他操作,您可以尝试以下操作:
gzip file && zcat file.gz | tail -n +300000001 | gzip > newFile.gz
这将首先将gzip
原始输入文件( file
) 创建file.gz
。然后,您zcat
新创建的file.gz
,通过管道tail -n +300000001
将其删除前 3M 行,压缩结果以节省磁盘空间并将其另存为newFile.gz
.这&&
确保您仅在gzip
操作成功时才继续(如果空间不足,操作将会失败)。
请注意,文本文件非常可压缩。例如,我使用 创建了一个测试文件seq 400000000 > file
,它打印从 1 到 400,000,000 的数字,这导致文件大小为 3.7G。当我使用上面的命令压缩它时,压缩文件只有849M,而newFile.gz
我创建的文件只有213M。
答案3
在某些文件系统(如 ext4 或 xfs)上,您可以使用fallocate()
系统调用。
答案4
如果您确实需要该任务,请再次投票给自定义程序。 C 或任何足够强大的动态语言(如 Perl 或 Python)都可以。我不会在这里写出源代码,但会描述在移动数据时防止数据丢失的算法:
- 从结尾处读取大文件,计算换行符。收集一些可以安全地放入可用空间的预定义数量的行后,将此块写入单独的文件并剪切大文件的尾部。使用块的文件名来存储行号。
- 之后,您将得到完全擦除的大文件和许多较小的文件占用相同的空间。
- 计算您的 3 亿行 - 您可以立即删除与不必要的行对应的所有块,因为您知道哪些块包含哪些行。
- 如果您实际上不需要大文件,则可以使用通配符或根据需要将它们串在一起,使用所需的任何工具直接对剩余块进行操作
cat
。 - 如果您毕竟需要大文件,并且在删除不必要的文件后释放的空间足以存储剩余块的总和 - 只需将它们与
cp
或组合在一起cat
。 - 如果您需要大文件并且没有足够的空间,请编写另一个小程序,该程序将执行与步骤 1 相反的操作:将列表和每个块的单独长度保存到某个列表文件。逐一读取块并将它们附加到新创建的“大文件”中。每次将块附加到大文件后,您将删除包含该块的单独小文件,从而允许您就地重新组装文件。如果您随时中断写入块的过程,您可以通过计算任何特定块的正确偏移量来重新开始写入大文件,因为您已经提前保存了每个块的大小。