我有一个很大的文件(〜400 GB),我需要从文件中删除最后两行。我尝试使用sed
,但是它运行了几个小时才放弃。有没有一种快速的方法,还是我坚持sed
?
我有一个很大的文件(〜400 GB),我需要从文件中删除最后两行。我尝试使用sed
,但是它运行了几个小时才放弃。有没有一种快速的方法,还是我坚持sed
?
Answers:
我没有在大文件上尝试过此操作,以查看它有多快,但是应该相当快。
要使用脚本从文件末尾删除行:
./shorten.py 2 large_file.txt
它查找文件的末尾,检查以确保最后一个字符是换行符,然后一次向后读取每个字符,直到找到三个换行符,然后在该点之后将其截断。更改已就位。
编辑:我在底部添加了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)
这是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)
这是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)
你可以尝试GNU头
head -n -2 file
head: illegal line count -- -2
我看到我的Debian Squeeze /测试系统(但Lenny / stable没有)包括“ truncate”命令作为“ coreutils”软件包的一部分。
有了它,你可以简单地做
truncate --size=-160 myfile
从文件末尾删除160个字节(显然,您需要确切地确定需要删除多少个字符)。
dd
脚本可以做到这一点(您需要指定输入偏移量以获取fe的最后一个千字节,然后使用tail -2 | LANG= wc -c
或sth之类的东西)。
tail
对于大型文件也非常有效-可以用于tail | wc -c
计算要修剪的字节数。
sed的问题在于它是一个流编辑器-即使您只想在末尾进行修改,它也会处理整个文件。因此,无论如何,您都在逐行创建一个新的400GB文件。任何对整个文件进行操作的编辑器都可能会出现此问题。
如果知道行数,则可以使用head
,但这又会创建一个新文件,而不是更改现有文件。我想,您可以从操作的简单性中获得速度上的提高。
您可能会比较幸运,可以使用split
将文件分成较小的部分,编辑最后一个部分,然后cat
再次使用它们进行组合,但是我不确定它是否会更好。我将使用字节数而不是行数,否则它可能根本不会更快-您仍将创建一个新的400GB文件。
尝试使用VIM ...我不确定它是否会成功,因为我从未在如此大的文件上使用过它,但过去我曾在较小的较大文件上使用过它,请尝试一下。
什么样的文件和什么格式?使用Perl之类的东西可能更容易,这取决于它是哪种文件-文本,图形,二进制文件?格式如何-CSV,TSV ...
如果您知道文件的大小为字节(例如400000000160),并且您知道需要准确删除160个字符以去除最后两行,那么类似
dd if=originalfile of=truncatedfile ibs=1 count=400000000000
应该可以。自从我用dd发怒以来已经有好几年了。我似乎记得,如果使用更大的块大小,事情进展得更快,但是是否能够做到这一点取决于您要删除的行是否是理想的倍数。
dd还有一些其他选项可以将文本记录填充到固定大小,这可能对初步通过很有用。
如果您喜欢Unix风格的解决方案,则可以使用三行代码(在Mac和Linux上进行测试)进行保存和交互式行截断。
小型+安全的Unix样式的行截断(要求确认):
n=2; file=test.csv; tail -n $n $file &&
read -p "truncate? (y/N)" -n1 key && [ "$key" == "y" ] &&
perl -e "truncate('$file', `wc -c <$file` - `tail -n $n $file | wc -c` )"
该解决方案依赖于一些常用的unix工具,但仍可perl -e "truncate(file,length)"
作为的最接近替代品truncate(1)
,并非在所有系统上都可用。
您还可以使用以下全面的可重复使用的Shell程序,该程序提供使用信息并具有截断确认,选项分析和错误处理的功能。
全面的行截断脚本:
#!/usr/bin/env bash
usage(){
cat <<-EOF
Usage: $0 [-n NUM] [-h] FILE
Options:
-n NUM number of lines to remove (default:1) from end of FILE
-h show this help
EOF
exit 1
}
num=1
for opt in $*; do case $opt in
-n) num=$2; shift;;
-h) usage; break;;
*) [ -f "$1" ] && file=$1; shift;;
esac done
[ -f "$file" ] || usage
bytes=`wc -c <$file`
size=`tail -n $num $file | wc -c`
echo "using perl 'truncate' to remove last $size of $bytes bytes:"
tail -n $num $file
read -p "truncate these lines? (y/N)" -n1 key && [ "$key" == "y" ] &&
perl -e "truncate('$file', $bytes - $size )"; echo ""
echo "new tail is:"; tail $file
这是一个用法示例:
$ cat data/test.csv
1 nice data
2 cool data
3 just data
GARBAGE to be removed (incl. empty lines above and below)
$ ./rmtail.sh -n 3 data/test.csv
using perl 'truncate' to remove last 60 of 96 bytes:
GARBAGE to be removed (incl. empty lines above and below)
truncate these lines? (y/N)y
new tail is:
1 nice data
2 cool data
3 just data
$ cat data/test.csv
1 nice data
2 cool data
3 just data
#!/ bin / sh ed“ $ 1” <<这里 $ d d w 这里
更改已就位。这比python脚本更简单,更高效。
ed
,执行时间是Python脚本的100倍。我只能想象OP的文件大小相差7000倍之多。
修改了接受的答案以解决类似的问题。可以稍微调整一下以删除n行。
import os
def clean_up_last_line(file_path):
"""
cleanup last incomplete line from a file
helps with an unclean shutdown of a program that appends to a file
if \n is not the last character, remove the line
"""
with open(file_path, 'r+b') as f:
f.seek(0, os.SEEK_END)
while f.tell() > 0: ## current position is greater than zero
f.seek(-1, os.SEEK_CUR)
if f.read(1) == '\n':
f.truncate()
break
f.seek(-1, os.SEEK_CUR) ## don't quite understand why this has to be called again, but it doesn't work without it
并进行相应的测试:
import unittest
class CommonUtilsTest(unittest.TestCase):
def test_clean_up_last_line(self):
"""
remove the last incomplete line from a huge file
a line is incomplete if it does not end with a line feed
"""
file_path = '/tmp/test_remove_last_line.txt'
def compare_output(file_path, file_data, expected_output):
"""
run the same test on each input output pair
"""
with open(file_path, 'w') as f:
f.write(file_data)
utils.clean_up_last_line(file_path)
with open(file_path, 'r') as f:
file_data = f.read()
self.assertTrue(file_data == expected_output, file_data)
## test a multiline file
file_data = """1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b
1362358458954466,2013-03-03 16:54:18,34.5,3.0,b
1362358630923094,2013-03-03 16:57:10,34.5,50.0,b
136235"""
expected_output = """1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b
1362358458954466,2013-03-03 16:54:18,34.5,3.0,b
1362358630923094,2013-03-03 16:57:10,34.5,50.0,b
"""
compare_output(file_path, file_data, expected_output)
## test a file with no line break
file_data = u"""1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b"""
expected_output = "1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b"
compare_output(file_path, file_data, expected_output)
## test a file a leading line break
file_data = u"""\n1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b"""
expected_output = "\n"
compare_output(file_path, file_data, expected_output)
## test a file with one line break
file_data = u"""1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b\n"""
expected_output = """1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b\n"""
compare_output(file_path, file_data, expected_output)
os.remove(file_path)
if __name__ == '__main__':
unittest.main()
head -n -2 file