Çok büyük bir dosyam (~ 400 GB) var ve son 2 satırı ondan kaldırmam gerekiyor. Kullanmaya çalıştım sed
, ama pes etmeden saatlerce sürdü. Bunu yapmanın hızlı bir yolu var mı, yoksa sıkışıp sed
mı kaldım ?
Çok büyük bir dosyam (~ 400 GB) var ve son 2 satırı ondan kaldırmam gerekiyor. Kullanmaya çalıştım sed
, ama pes etmeden saatlerce sürdü. Bunu yapmanın hızlı bir yolu var mı, yoksa sıkışıp sed
mı kaldım ?
Yanıtlar:
Bunu ne kadar hızlı olduğunu görmek için büyük bir dosyada denemedim, ancak oldukça hızlı olması gerekiyor.
Bir dosyanın sonundan satırları kaldırmak için komut dosyasını kullanmak için:
./shorten.py 2 large_file.txt
Dosyanın sonuna bakar, son karakterin yeni bir satır olduğundan emin olmak için kontrol eder, ardından üç karakter satırı bulunana kadar her bir karakteri bir defa geriye doğru okur ve bu noktadan hemen sonra dosyayı keser. Değişiklik yapıldı.
Düzenleme: En altta bir Python 2.4 sürümü ekledim.
İşte Python 2.5 / 2.6 için bir sürüm:
#!/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)
İşte bir Python 3 versiyonu:
#!/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)
İşte bir Python 2.4 versiyonu:
#!/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 kafasını deneyebilirsin
head -n -2 file
head: illegal line count -- -2
Debian Squeeze / Test sistemlerimin (fakat Lenny / stable değil) "coreutils" paketinin bir parçası olarak "truncate" komutunu içerdiğini görüyorum.
Bununla beraber sadece bir şey yapabilirdi
truncate --size=-160 myfile
Dosyanın sonundan 160 bayt kaldırmak için (açıkçası kaldırmanız gereken tam olarak kaç karakter bulmanız gerekiyor).
dd
betiğin bunu yapacağını tahmin ediyorum tail -2 | LANG= wc -c
.
tail
büyük dosyalar için de etkilidir - tail | wc -c
kırpılacak bayt sayısını hesaplamak için kullanılabilir.
Sed ile ilgili sorun bunun bir akış editörü olması - sadece sonuna kadar değişiklik yapmak isteseniz bile tüm dosyayı işleyecektir. Yani ne olursa olsun, satır satır satır yeni bir 400GB dosya oluşturuyorsunuz. Tüm dosya üzerinde çalışan herhangi bir editör muhtemelen bu soruna sahip olacaktır.
Satır sayısını biliyorsanız, kullanabilirsiniz head
, ancak bu, mevcut olanı değiştirmek yerine yeni bir dosya oluşturur. Sanırım, eylemin basitliğinden hız kazanabilirsiniz.
Sen belki kullanarak daha şanslı split
kullanarak sonra sonuncuyu düzenleme ve küçük parçalar halinde dosya kırmak için cat
onları tekrar birleştirmek, ancak herhangi bir iyi olacak olmadığından emin değilim. Satırlar yerine bayt sayıları kullanırdım, aksi takdirde muhtemelen daha hızlı olmaz - hala yeni bir 400GB dosya oluşturacaksınız.
VIM'i deneyin ... Hile yapıp yapmayacağından emin değilim, çünkü bu kadar büyük bir dosyada hiç kullanmamıştım, ancak geçmişte daha küçük dosyalarda kullanmıştım.
Ne tür bir dosya ve hangi biçimde? Ne tür bir dosya olduğuna bağlı olarak Perl gibi bir şey kullanmak daha kolay olabilir mi - metin, grafik, ikili? Nasıl biçimlendirilir - CSV, TSV ...
Dosyanın boyutunu bayt olarak biliyorsanız (400000000160 diyelim) ve son iki satırı sıyırmak için tam olarak 160 karakteri silmeniz gerektiğini biliyorsanız,
dd if=originalfile of=truncatedfile ibs=1 count=400000000000
hile yapmalı. GG'yi öfkeyle kullandığımdan beri yıllar geçti; Daha büyük bir blok boyutu kullanıyorsanız, işlerin daha hızlı gittiğini hatırlıyor gibiyim, ancak bunu yapıp yapmamanız, bırakmak istediğiniz çizgilerin hoş bir katında olup olmadığına bağlı.
dd, metin kayıtlarını ön geçiş olarak yararlı olabilecek sabit bir boyuta yapıştırmak için başka seçeneklere de sahiptir.
"Truncate" komutu sisteminizde mevcut değilse (diğer cevabımı inceleyin), sistem çağrısının belirtilen uzunlukta bir dosyayı kesmesi için "man 2 truncate" konusuna bakın.
Açıkçası, dosyayı kısaltmanız gereken karakter sayısını bilmeniz gerekir (boyut eksi sorunun uzunluğu iki satır; herhangi bir cr / lf karakteri saymayı unutmayın).
Ve bunu denemeden önce dosyayı yedekleyin!
Unix tarzı çözümler tercih ederseniz, üç satır kod kullanarak (Mac ve Linux'ta test edilmiştir) kaydetme ve etkileşimli satır kesmeye sahip olabilirsiniz.
küçük + güvenli unix tarzı çizgi kesmesi (onay için sorar):
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` )"
Bu çözüm birkaç ortak unix-alete dayanmaktadır, ancak yine de tüm sistemlerde bulunmayan perl -e "truncate(file,length)"
en yakın yerine truncate(1)
kullanılmaktadır.
Kullanım bilgisi sağlayan ve kesme onayı, seçenek ayrıştırma ve hata yönetimi özelliklerine sahip aşağıdaki kapsamlı yeniden kullanılabilir kabuk programını da kullanabilirsiniz.
kapsamlı satır kesme komut dosyası :
#!/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
İşte bir kullanım örneği:
$ 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" << BURAYA $ d d w İŞTE
değişiklikler yapıldı. Bu python betiğinden daha basit ve daha etkilidir.
ed
Python komut dizimden daha uzun sürdüm . OP'nin 7000 kat daha büyük olan dosyası için ne kadar fazla fark olacağını hayal edebiliyorum.
Benzer bir sorunu çözmek için kabul edilen cevabı değiştirildi. N satırlarını kaldırmak için biraz tweaked olabilir.
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
Ve ilgili test:
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()
Vim'i Ex modunda kullanabilirsiniz:
ex -sc '-,d|x' file
-,
son 2 satırı seç
d
silmek
x
kaydet ve kapat
head -n -2 file