非常に大きなファイル(約400 GB)があり、最後の2行を削除する必要があります。を使用しようとしましsed
たが、何時間も実行してからbeforeめました。これを行う簡単な方法はありsed
ますか?
非常に大きなファイル(約400 GB)があり、最後の2行を削除する必要があります。を使用しようとしましsed
たが、何時間も実行してからbeforeめました。これを行う簡単な方法はありsed
ますか?
回答:
私は、それがどれほど速いかを確認するために大きなファイルでこれを試していませんが、かなり速いはずです。
スクリプトを使用してファイルの末尾から行を削除するには:
./shorten.py 2 large_file.txt
ファイルの最後までシークし、最後の文字が改行であることを確認してから、3文字の改行が見つかるまで逆方向に各文字を1つずつ読み取り、そのポイントの直後でファイルを切り捨てます。変更はその場で行われます。
編集:下部に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を試すことができます
head -n -2 file
head: illegal line count -- -2
Debian Squeeze / testingシステム(Lenny / stableを除く)には、「coreutils」パッケージの一部として「truncate」コマンドが含まれています。
それを使えば、次のようなことができます。
truncate --size=-160 myfile
ファイルの末尾から160バイトを削除するには(明らかに、削除する必要がある文字数を正確に把握する必要があります)。
dd
スクリプトで実行できます(入力オフセットを指定して、最後のキロバイトを取得してからtail -2 | LANG= wc -c
、またはそのようなsthを使用する必要があります)。
tail
また、大きなファイルに対しても効率的ですtail | wc -c
。トリミングするバイト数の計算に使用できます。
sedの問題は、それがストリームエディタであるということです。最後の近くでのみ変更を加えたい場合でも、ファイル全体を処理します。だから、あなたは新しい400GBファイルを1行ずつ作成しています。ファイル全体を操作するエディターには、おそらくこの問題があります。
行数がわかっている場合は、を使用できますhead
が、既存のファイルを変更せずに新しいファイルを作成します。アクションのシンプルさから速度が向上するかもしれません。
あなたは可能性が使用して、より良い運を持ってsplit
、小さな部分にファイルを分割する最後のものを編集して、使用してcat
それらを再び結合するが、それはどんな良くなるかどうかはわかりません。行ではなくバイトカウントを使用します。そうしないと、おそらくまったく高速になりません。新しい400GBファイルを作成することになります。
VIMを試してみてください...このような大きなファイルで使用したことがないので、うまくいくかどうかはわかりませんが、過去に小さなファイルで使用したことがあります。
ファイルのサイズがバイト(400000000160と言う)までわかっていて、最後の2行を削除するためにちょうど160文字を削除する必要があることがわかっている場合は、
dd if=originalfile of=truncatedfile ibs=1 count=400000000000
トリックを行う必要があります。私が怒りでddを使用して以来、それは時代です。大きなブロックサイズを使用すると高速化することを覚えているようですが、それができるかどうかは、ドロップする行が適切な倍数であるかどうかによって異なります。
ddには、予備のパスとして役立つ可能性のある固定サイズにテキストレコードをパディングする他のオプションがあります。
UNIXスタイルのソリューションを好む場合は、3行のコードを使用して保存およびインタラクティブな行の切り捨てを行うことができます(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-toolsに依存していますが、すべてのシステムで利用できるわけではない、の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 ed "$ 1" <<こちら $ d d w ここに
変更は適切に行われます。これは、pythonスクリプトよりも簡単で効率的です。
ed
実行にPythonスクリプトの100倍の時間がかかりました。7000倍の大きさのOPのファイルの違いがどれだけ大きいか想像することができます。
同様の問題を解決するために受け入れられた答えを修正しました。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