Pembaruan: Saya telah mengganti kode asli dengan versi yang diperbarui dengan banyak perbaikan. Sebut ini kualitas alpha (aktual?).
Versi ini termasuk:
- penanganan opsi baris perintah
- validasi format tanggal baris perintah
- beberapa
try
blok
- membaca garis pindah ke fungsi
Teks asli:
Nah, apa yang kamu tahu? "Carilah" dan kamu akan menemukan! Berikut adalah program Python yang mencari-cari di dalam file dan menggunakan pencarian biner yang kurang lebih. Ini jauh lebih cepat daripada naskah AWK yang ditulis orang lain .
Ini (pra?) Berkualitas alpha. Itu harus memiliki try
blok dan validasi input dan banyak pengujian dan tidak diragukan lagi bisa lebih Pythonic. Tapi ini untuk hiburanmu. Oh, dan ini ditulis untuk Python 2.6.
Kode baru:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# timegrep.py by Dennis Williamson 20100113
# in response to http://serverfault.com/questions/101744/fast-extraction-of-a-time-range-from-syslog-logfile
# thanks to serverfault user http://serverfault.com/users/1545/mike
# for the inspiration
# Perform a binary search through a log file to find a range of times
# and print the corresponding lines
# tested with Python 2.6
# TODO: Make sure that it works if the seek falls in the middle of
# the first or last line
# TODO: Make sure it's not blind to a line where the sync read falls
# exactly at the beginning of the line being searched for and
# then gets skipped by the second read
# TODO: accept arbitrary date
# done: add -l long and -s short options
# done: test time format
version = "0.01a"
import os, sys
from stat import *
from datetime import date, datetime
import re
from optparse import OptionParser
# Function to read lines from file and extract the date and time
def getdata():
"""Read a line from a file
Return a tuple containing:
the date/time in a format such as 'Jan 15 20:14:01'
the line itself
The last colon and seconds are optional and
not handled specially
"""
try:
line = handle.readline(bufsize)
except:
print("File I/O Error")
exit(1)
if line == '':
print("EOF reached")
exit(1)
if line[-1] == '\n':
line = line.rstrip('\n')
else:
if len(line) >= bufsize:
print("Line length exceeds buffer size")
else:
print("Missing newline")
exit(1)
words = line.split(' ')
if len(words) >= 3:
linedate = words[0] + " " + words[1] + " " + words[2]
else:
linedate = ''
return (linedate, line)
# End function getdata()
# Set up option handling
parser = OptionParser(version = "%prog " + version)
parser.usage = "\n\t%prog [options] start-time end-time filename\n\n\
\twhere times are in the form hh:mm[:ss]"
parser.description = "Search a log file for a range of times occurring yesterday \
and/or today using the current time to intelligently select the start and end. \
A date may be specified instead. Seconds are optional in time arguments."
parser.add_option("-d", "--date", action = "store", dest = "date",
default = "",
help = "NOT YET IMPLEMENTED. Use the supplied date instead of today.")
parser.add_option("-l", "--long", action = "store_true", dest = "longout",
default = False,
help = "Span the longest possible time range.")
parser.add_option("-s", "--short", action = "store_true", dest = "shortout",
default = False,
help = "Span the shortest possible time range.")
parser.add_option("-D", "--debug", action = "store", dest = "debug",
default = 0, type = "int",
help = "Output debugging information.\t\t\t\t\tNone (default) = %default, Some = 1, More = 2")
(options, args) = parser.parse_args()
if not 0 <= options.debug <= 2:
parser.error("debug level out of range")
else:
debug = options.debug # 1 = print some debug output, 2 = print a little more, 0 = none
if options.longout and options.shortout:
parser.error("options -l and -s are mutually exclusive")
if options.date:
parser.error("date option not yet implemented")
if len(args) != 3:
parser.error("invalid number of arguments")
start = args[0]
end = args[1]
file = args[2]
# test for times to be properly formatted, allow hh:mm or hh:mm:ss
p = re.compile(r'(^[2][0-3]|[0-1][0-9]):[0-5][0-9](:[0-5][0-9])?$')
if not p.match(start) or not p.match(end):
print("Invalid time specification")
exit(1)
# Determine Time Range
yesterday = date.fromordinal(date.today().toordinal()-1).strftime("%b %d")
today = datetime.now().strftime("%b %d")
now = datetime.now().strftime("%R")
if start > now or start > end or options.longout or options.shortout:
searchstart = yesterday
else:
searchstart = today
if (end > start > now and not options.longout) or options.shortout:
searchend = yesterday
else:
searchend = today
searchstart = searchstart + " " + start
searchend = searchend + " " + end
try:
handle = open(file,'r')
except:
print("File Open Error")
exit(1)
# Set some initial values
bufsize = 4096 # handle long lines, but put a limit them
rewind = 100 # arbitrary, the optimal value is highly dependent on the structure of the file
limit = 75 # arbitrary, allow for a VERY large file, but stop it if it runs away
count = 0
size = os.stat(file)[ST_SIZE]
beginrange = 0
midrange = size / 2
oldmidrange = midrange
endrange = size
linedate = ''
pos1 = pos2 = 0
if debug > 0: print("File: '{0}' Size: {1} Today: '{2}' Now: {3} Start: '{4}' End: '{5}'".format(file, size, today, now, searchstart, searchend))
# Seek using binary search
while pos1 != endrange and oldmidrange != 0 and linedate != searchstart:
handle.seek(midrange)
linedate, line = getdata() # sync to line ending
pos1 = handle.tell()
if midrange > 0: # if not BOF, discard first read
if debug > 1: print("...partial: (len: {0}) '{1}'".format((len(line)), line))
linedate, line = getdata()
pos2 = handle.tell()
count += 1
if debug > 0: print("#{0} Beg: {1} Mid: {2} End: {3} P1: {4} P2: {5} Timestamp: '{6}'".format(count, beginrange, midrange, endrange, pos1, pos2, linedate))
if searchstart > linedate:
beginrange = midrange
else:
endrange = midrange
oldmidrange = midrange
midrange = (beginrange + endrange) / 2
if count > limit:
print("ERROR: ITERATION LIMIT EXCEEDED")
exit(1)
if debug > 0: print("...stopping: '{0}'".format(line))
# Rewind a bit to make sure we didn't miss any
seek = oldmidrange
while linedate >= searchstart and seek > 0:
if seek < rewind:
seek = 0
else:
seek = seek - rewind
if debug > 0: print("...rewinding")
handle.seek(seek)
linedate, line = getdata() # sync to line ending
if debug > 1: print("...junk: '{0}'".format(line))
linedate, line = getdata()
if debug > 0: print("...comparing: '{0}'".format(linedate))
# Scan forward
while linedate < searchstart:
if debug > 0: print("...skipping: '{0}'".format(linedate))
linedate, line = getdata()
if debug > 0: print("...found: '{0}'".format(line))
if debug > 0: print("Beg: {0} Mid: {1} End: {2} P1: {3} P2: {4} Timestamp: '{5}'".format(beginrange, midrange, endrange, pos1, pos2, linedate))
# Now that the preliminaries are out of the way, we just loop,
# reading lines and printing them until they are
# beyond the end of the range we want
while linedate <= searchend:
print line
linedate, line = getdata()
if debug > 0: print("Start: '{0}' End: '{1}'".format(searchstart, searchend))
handle.close()
if debug > 0: print("File: '{0}' Size: {1} Today: '{2}' Now: {3} Start: '{4}' End: '{5}'".format(file, size, today, now, searchstar$
. Apakahsearchstar
seharusnya diakhiri dengan$
, atau apakah itu salah cetak? Saya mendapatkan kesalahan sintaks pada baris ini (Baris 159)))
.$
alih-alih seharusnyat, searchend))
begitu katanya... searchstart, searchend))
Dari pencarian cepat di internet, ada hal-hal yang diekstrak berdasarkan kata kunci (seperti KEBAKARAN atau semacamnya :) tetapi tidak ada yang mengekstrak rentang tanggal dari file.
Tampaknya tidak sulit untuk melakukan apa yang Anda usulkan:
Tampaknya lurus ke depan, dan saya bisa menulis untuk Anda jika Anda tidak keberatan Ruby :)
sumber
Ini akan mencetak rentang entri antara waktu mulai dan waktu selesai berdasarkan bagaimana mereka berhubungan dengan waktu saat ini ("sekarang").
Pemakaian:
Contoh:
Opsi
-l
(panjang) menyebabkan output terpanjang yang mungkin. Waktu mulai akan ditafsirkan sebagai kemarin jika nilai jam dan menit dari waktu mulai kurang dari waktu akhir dan sekarang. Waktu akhir akan ditafsirkan sebagai hari ini jika waktu mulai dan waktu selesai HH: Nilai MM lebih besar dari "sekarang".Dengan asumsi bahwa "sekarang" adalah "11 Januari 19:00", ini adalah bagaimana berbagai contoh waktu mulai dan akhir akan ditafsirkan (tanpa
-l
kecuali sebagaimana disebutkan):Hampir semua skrip diatur. Dua baris terakhir melakukan semua pekerjaan.
Peringatan: tidak ada validasi argumen atau pengecekan kesalahan yang dilakukan. Kasing tepi belum diuji secara menyeluruh. Ini ditulis menggunakan
gawk
versi lain dari AWK mungkin berkotek.Saya pikir AWK sangat efisien dalam mencari file. Saya tidak berpikir hal lain akan lebih cepat dalam mencari file teks yang tidak diindeks .
sumber
Program C ++ yang menerapkan pencarian biner - perlu beberapa modifikasi sederhana (mis. Memanggil strptime) untuk bekerja dengan tanggal teks.
http://gitorious.org/bs_grep/
Saya memiliki versi sebelumnya dengan dukungan untuk tanggal teks, namun masih terlalu lambat untuk skala file log kami; profiling mengatakan bahwa lebih dari 90% dari waktu dihabiskan dalam strptime, jadi, kami hanya memodifikasi format log untuk menyertakan cap waktu unix numerik juga.
sumber
Meskipun jawaban ini terlalu terlambat, mungkin bermanfaat bagi sebagian orang.
Saya telah mengubah kode dari @Dennis Williamson menjadi kelas Python yang dapat digunakan untuk hal-hal python lainnya.
Saya telah menambahkan dukungan untuk banyak dukungan tanggal.
sumber