如何从一个 700?GB 的文本文件中删除前 3 亿行,该系统的总磁盘空间为 1?TB,可用空间为 300?GB?(我的系统有 2?GB 的内存。)我找到的答案使用 sed、tail、head:
但我认为(请纠正我)我无法使用它们,因为磁盘空间被限制为 1?TB,并且它们在处理过程中生成一个新文件和/或有一个 tmp 文件。
该文件包含 JSON 格式的数据库记录。
fro*_*utz 158
可以使用dd(或使用 loop devices)就地删除前 n 行(或字节)。它不使用临时文件并且没有大小限制;但是,这很危险,因为没有进度跟踪,任何错误都会使您的文件损坏。
示例:创建一个 1000 行的示例文件:
$ seq 1 1000 > 1000lines.txt
$ head -n 3 1000lines.txt
1
2
3
$ tail -n 3 1000lines.txt
998
999
1000
Run Code Online (Sandbox Code Playgroud)
我们要删除前 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
Run Code Online (Sandbox Code Playgroud)
该文件是 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
Run Code Online (Sandbox Code Playgroud)
这将删除前 300 行,但现在重复最后 1092 个字节,因为文件尚未截断:
$ truncate -s 2801 1000lines.txt
Run Code Online (Sandbox Code Playgroud)
这将文件缩小到其最终大小,删除文件末尾的重复行。
结果:
$ stat -c %s 1000lines.txt
2801
$ head -n 3 1000lines.txt
301
302
303
$ tail -n 3 1000lines.txt
998
999
1000
Run Code Online (Sandbox Code Playgroud)
处理较大文件的过程类似。您可能需要设置更大的块大小以获得更好的性能(块大小选项dd是bs)。
主要问题是确定确切行号的正确字节偏移量。一般只能通过阅读和计数来完成。使用这种方法,即使您丢弃了大量文件,您也必须至少读取整个文件一次。
ter*_*don 130
如果您有足够的空间来压缩文件,这应该会释放大量空间,允许您执行其他操作,您可以尝试以下操作:
gzip file && zcat file.gz | tail -n +300000001 | gzip > newFile.gz
Run Code Online (Sandbox Code Playgroud)
这将首先创建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。
pin*_*ime 38
在某些文件系统(如 ext4 或 xfs)上,您可以使用fallocate()系统调用。
fro*_*utz 31
您可以使用losetup,作为dd此处描述的方法的替代方法。同样,这种方法同样危险。
同样,相同的测试文件和大小(从 1000 行文件中删除第 1-300 行):
$ seq 1 1000 > 1000lines.txt
$ 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
Run Code Online (Sandbox Code Playgroud)
创建循环设备:
# losetup --find --show 1000lines.txt
/dev/loop0
losetup: 1000lines.txt: \
Warning: file does not fit into a 512-byte sector; \
the end of the file will be ignored.
# head -n 3 /dev/loop0
1
2
3
# tail -n 3 /dev/loop0
921
922
923
Run Code Online (Sandbox Code Playgroud)
哎呀。缺少数字。这是怎么回事?
循环设备要求它们的后备文件是扇区大小的倍数。带行的文本文件通常不适合该方案,因此为了不错过文件末尾(最后一部分扇区)的内容,只需先附加一些数据,然后再试一次:
# head -c 512 /dev/zero >> 1000lines.txt
# losetup --find --show 1000lines.txt
/dev/loop1
losetup: 1000lines.txt: \
Warning: file does not fit into a 512-byte sector; \
the end of the file will be ignored.
# tail -n 3 /dev/loop1
999
1000
\0
Run Code Online (Sandbox Code Playgroud)
警告仍然存在,但内容现在已完成,所以没关系。
创建另一个,这次使用 300 行偏移:
# losetup --find --show --offset=1092 1000lines.txt
/dev/loop2
losetup: 1000lines.txt: \
Warning: file does not fit into a 512-byte sector; \
the end of the file will be ignored.
# head -n 3 /dev/loop2
301
302
303
# tail -n 3 /dev/loop2
999
1000
\0
Run Code Online (Sandbox Code Playgroud)
这是关于循环设备的好处。您不必担心会意外截断文件。您还可以在执行任何操作之前轻松验证您的偏移量是否确实正确。
最后,只需将其从偏移设备复制到完整:
cp /dev/loop2 /dev/loop1
Run Code Online (Sandbox Code Playgroud)
溶解循环装置:
losetup -d /dev/loop2 /dev/loop1 /dev/loop0
Run Code Online (Sandbox Code Playgroud)
(或:losetup -D解散所有循环设备。)
截断文件以达到目标文件大小:
truncate -s 2801 1000lines.txt
Run Code Online (Sandbox Code Playgroud)
结果:
$ head -n 3 1000lines.txt
301
302
303
$ tail -n 3 1000lines.txt
998
999
1000
Run Code Online (Sandbox Code Playgroud)
Ole*_*kov 19
如果您确实需要该任务,请再次投票支持自定义程序。C 或任何足够强大的动态语言,如 Perl 或 Python 都可以。我不会在这里写出源代码,但会描述在移动数据时防止数据丢失的算法:
cat根据需要将它们串在一起,使用您需要的任何工具直接对剩余的块进行操作。cp或组合在一起cat。与ksh93:
tail -n +300000001 < file 1<>; file
Run Code Online (Sandbox Code Playgroud)
该1<>;操作符是标准1<>操作符(以读+写模式打开而不会截断)的特定于 ksh93 的变体,如果该命令成功,它会在命令返回到命令离开其标准输出的位置后截断文件。
使用其他外壳,您始终可以手动执行就地截断perl,例如:
{
tail -n +300000001 &&
perl -e 'truncate STDOUT, tell STDOUT'
} < file 1<> file
Run Code Online (Sandbox Code Playgroud)
要获得进度条,请使用pv:
{
head -n 300000000 | pv -s 300000000 -lN 'Skipping 300M lines' > /dev/null &&
cat | pv -N 'Rewriting the rest' &&
perl -e 'truncate STDOUT, tell STDOUT'
} < file 1<> file
Run Code Online (Sandbox Code Playgroud)
(如果它的输入和输出指向同一个文件,使用head | pvand cat | pvaspv将拒绝工作。pv -Sls 300000000也不会工作,因为pv在现有的第 300000000 行之后没有将指针留在文件中,就像head这样(并且是 POSIX 要求的)对于可查找的文件)pv | cat而不是cat | pv允许pv知道它需要读取多少并给你一个预计到达时间,但它目前是假的,因为它没有考虑到它没有从文件的开头读取的情况这里的情况)。
请注意,这些是危险的,因为文件正在被覆盖。如果前 300M 行包含空洞(对于有效的文本文件不应该发生),则可能会耗尽磁盘空间,并且文件的其余部分占用的空间比您在 FS 上的空闲空间多。