有效删除超大文本文件的最后两行

Rus*_*rry 31 linux sed text-manipulation

我有一个非常大的文件(~400 GB),我需要从中删除最后两行。我尝试使用sed,但在我放弃之前它运行了几个小时。有没有一种快速的方法可以做到这一点,还是我坚持sed

Den*_*son 31

我还没有在大文件上试过这个,看看它有多快,但它应该相当快。

要使用脚本从文件末尾删除行:

./shorten.py 2 large_file.txt
Run Code Online (Sandbox Code Playgroud)

它寻找到文件的末尾,检查以确保最后一个字符是换行符,然后一次一个地读取每个字符,直到找到三个换行符并在该点之后截断文件。更改已就位。

编辑:我在底部添加了一个 Python 2.4 版本。

这是 Python 2.5/2.6 的版本:

#!/usr/bin/env python2.5
from __future__ import with_statement
# also tested with Python 2.6

import os, sys

if len(sys.argv) != 3:
    print sys.argv[0] + ": Invalid number of arguments."
    print "Usage: " + sys.argv[0] + " linecount filename"
    print "to remove linecount lines from the end of the file"
    exit(2)

number = int(sys.argv[1])
file = sys.argv[2]
count = 0

with open(file,'r+b') as f:
    f.seek(0, os.SEEK_END)
    end = f.tell()
    while f.tell() > 0:
        f.seek(-1, os.SEEK_CUR)
        char = f.read(1)
        if char != '\n' and f.tell() == end:
            print "No change: file does not end with a newline"
            exit(1)
        if char == '\n':
            count += 1
        if count == number + 1:
            f.truncate()
            print "Removed " + str(number) + " lines from end of file"
            exit(0)
        f.seek(-1, os.SEEK_CUR)

if count < number + 1:
    print "No change: requested removal would leave empty file"
    exit(3)
Run Code Online (Sandbox Code Playgroud)

这是一个 Python 3 版本:

#!/usr/bin/env python3.0

import os, sys

if len(sys.argv) != 3:
    print(sys.argv[0] + ": Invalid number of arguments.")
    print ("Usage: " + sys.argv[0] + " linecount filename")
    print ("to remove linecount lines from the end of the file")
    exit(2)

number = int(sys.argv[1])
file = sys.argv[2]
count = 0

with open(file,'r+b', buffering=0) as f:
    f.seek(0, os.SEEK_END)
    end = f.tell()
    while f.tell() > 0:
        f.seek(-1, os.SEEK_CUR)
        print(f.tell())
        char = f.read(1)
        if char != b'\n' and f.tell() == end:
            print ("No change: file does not end with a newline")
            exit(1)
        if char == b'\n':
            count += 1
        if count == number + 1:
            f.truncate()
            print ("Removed " + str(number) + " lines from end of file")
            exit(0)
        f.seek(-1, os.SEEK_CUR)

if count < number + 1:
    print("No change: requested removal would leave empty file")
    exit(3)
Run Code Online (Sandbox Code Playgroud)

这是一个 Python 2.4 版本:

#!/usr/bin/env python2.4

import sys

if len(sys.argv) != 3:
    print sys.argv[0] + ": Invalid number of arguments."
    print "Usage: " + sys.argv[0] + " linecount filename"
    print "to remove linecount lines from the end of the file"
    sys.exit(2)

number = int(sys.argv[1])
file = sys.argv[2]
count = 0
SEEK_CUR = 1
SEEK_END = 2

f = open(file,'r+b')
f.seek(0, SEEK_END)
end = f.tell()

while f.tell() > 0:
    f.seek(-1, SEEK_CUR)
    char = f.read(1)
    if char != '\n' and f.tell() == end:
        print "No change: file does not end with a newline"
        f.close()
        sys.exit(1)
    if char == '\n':
        count += 1
    if count == number + 1:
        f.truncate()
        print "Removed " + str(number) + " lines from end of file"
        f.close()
        sys.exit(0)
    f.seek(-1, SEEK_CUR)

if count < number + 1:
    print "No change: requested removal would leave empty file"
    f.close()
    sys.exit(3)
Run Code Online (Sandbox Code Playgroud)


use*_*894 12

你可以试试 GNU head

head -n -2 file
Run Code Online (Sandbox Code Playgroud)

  • @PetrMarek 和其他人:问题在于它涉及 _giant_ 文件。该解决方案需要通过管道将整个文件送入并将所有数据重写到新位置——问题的重点是避免这种情况。需要就地解决方案,例如已接受答案中的解决方案。 (6认同)
  • @SooDesuNe:不,它会按照手册打印从开头到结尾的 2 行的所有行。然而,这需要重定向到一个文件,然后这个文件是巨大的问题,所以它不是这个问题的完美解决方案。 (2认同)

tim*_*day 7

我看到我的 Debian Squeeze/测试系统(但不是 Lenny/stable)包含一个“truncate”命令作为“coreutils”包的一部分。

有了它,你可以简单地做类似的事情

truncate --size=-160 myfile
Run Code Online (Sandbox Code Playgroud)

从文件末尾删除 160 个字节(显然你需要弄清楚你需要删除多少个字符)。


Zac*_*son 6

sed 的问题在于它是一个流编辑器——即使您只想在接近尾声时进行修改,它也会处理整个文件。因此,无论如何,您都是在逐行创建一个新的 400GB 文件。任何对整个文件进行操作的编辑器都可能有这个问题。

如果您知道行数,则可以使用head,但这同样会创建一个新文件,而不是更改现有文件。我猜您可能会从操作的简单性中获得速度提升。

可能会更幸运地使用split将文件分成更小的部分,编辑最后一个,然后cat再次组合它们,但我不确定它是否会更好。我会使用字节数而不是行数,否则它可能根本不会更快——您仍将创建一个新的 400GB 文件。