매우 큰 파일 (~ 400GB)이 있으며 마지막 2 줄을 제거해야합니다. 나는을 사용하려고했지만 sed
포기하기 전에 몇 시간 동안 달렸다. 이 작업을 수행하는 빠른 방법이 sed
있습니까 , 아니면 붙어 있습니까?
매우 큰 파일 (~ 400GB)이 있으며 마지막 2 줄을 제거해야합니다. 나는을 사용하려고했지만 sed
포기하기 전에 몇 시간 동안 달렸다. 이 작업을 수행하는 빠른 방법이 sed
있습니까 , 아니면 붙어 있습니까?
답변:
나는 그것이 얼마나 빠른지 알기 위해 큰 파일에서 이것을 시도하지는 않았지만 상당히 빠릅니다.
스크립트를 사용하여 파일 끝에서 줄을 제거하려면
./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
데비안 스퀴즈 / 테스트 시스템 (Lenny / stable 제외)에는 "coreutils"패키지의 일부로 "truncate"명령이 포함되어 있습니다.
그것으로 당신은 단순히 같은 것을 할 수 있습니다
truncate --size=-160 myfile
파일 끝에서 160 바이트를 제거하려면 (제거 해야하는 문자 수를 정확히 파악해야합니다).
dd
스크립트가 수행 할 I / guess / (마지막 킬로바이트를 얻은 다음 tail -2 | LANG= wc -c
, 또는 sth 를 사용하려면 입력 오프셋을 지정해야 합니다).
tail
큰 파일에도 효율적입니다 tail | wc -c
. 트리밍 할 바이트 수를 계산 하는 데 사용할 수 있습니다 .
sed의 문제점은 스트림 편집기라는 것입니다. 끝 부분 만 수정하고 싶더라도 전체 파일을 처리합니다. 따라서 무엇이든 상관없이 새로운 400GB 파일을 한 줄씩 작성합니다. 전체 파일에서 작동하는 편집기에는 아마도이 문제가있을 것입니다.
줄 수를 알고 있다면을 사용할 수 head
있지만 다시 기존 파일을 변경하는 대신 새 파일을 만듭니다. 작업의 단순성으로 속도가 향상 될 수 있습니다.
당신은 수도 사용하여 더 나은 운이 split
사용 후 마지막 편집하고, 작은 조각으로 파일을 깰 cat
다시 결합하지만 더 나은 될 것입니다 있는지 확실하지 않습니다. 줄보다는 바이트 수를 사용합니다. 그렇지 않으면 전혀 빠르지 않을 것입니다-여전히 새로운 400GB 파일을 만들 것입니다.
VIM을 사용해보십시오 ... 큰 파일에 사용한 적이 없기 때문에 트릭을 수행할지 확실하지 않지만 과거에는 더 작은 파일에 사용했지만 시도해 보았습니다.
어떤 종류의 파일이며 어떤 형식입니까? 텍스트, 그래픽, 바이너리 등의 파일 종류에 따라 Perl과 같은 것을 사용하는 것이 더 쉬울 수 있습니다. CSV, TSV ... 형식은 어떻게됩니까?
파일 크기를 바이트 (400000000160 say)로 알고 마지막 두 줄을 제거하기 위해 정확히 160자를 제거해야한다는 것을 알고 있다면
dd if=originalfile of=truncatedfile ibs=1 count=400000000000
트릭을해야합니다. dd를 화나게 사용한 지 오래되었습니다. 더 큰 블록 크기를 사용하면 상황이 더 빨라진다는 것을 기억하는 것 같습니다. 그러나 그렇게 할 수 있는지 여부는 놓을 선이 좋은 배수인지 여부에 따라 다릅니다.
dd에는 텍스트 레코드를 고정 크기로 채우는 예비 옵션으로 유용 할 수있는 다른 옵션이 있습니다.
유닉스 스타일 솔루션을 선호하는 경우 세 줄의 코드 (Mac 및 Linux에서 테스트 됨)를 사용하여 저장 및 대화식 줄 잘림을 수행 할 수 있습니다.
작고 안전한 유닉스 스타일의 줄 잘림 (확인 요청) :
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` )"
이 솔루션은 몇 가지 일반적인 유닉스 도구를 사용하지만 여전히 모든 시스템에서 사용할 수있는 perl -e "truncate(file,length)"
가장 가까운 대체 도구로 사용 truncate(1)
합니다.
사용 정보를 제공하고 잘림 확인, 옵션 구문 분석 및 오류 처리 기능을 제공하는 다음과 같은 포괄적 인 재사용 가능한 셸 프로그램을 사용할 수도 있습니다.
포괄적 인 줄 잘림 스크립트 :
#!/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 에드 "$ 1"<< 여기 $ 디 디 승 이리
변경이 이루어집니다. 이것은 파이썬 스크립트보다 간단하고 효율적입니다.
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