Что такое pythonic способ просмотра хвоста растущего файла для появления определенных ключевых слов?
В оболочке я могу сказать:
tail -f "$file" | grep "$string" | while read hit; do
#stuff
done
Что такое pythonic способ просмотра хвоста растущего файла для появления определенных ключевых слов?
В оболочке я могу сказать:
tail -f "$file" | grep "$string" | while read hit; do
#stuff
done
Ну, самым простым способом было бы постоянно читать из файла, проверять, что нового и проверить для хитов.
import time
def watch(fn, words):
fp = open(fn, 'r')
while True:
new = fp.readline()
# Once all lines are read this just returns ''
# until the file changes and a new line appears
if new:
for word in words:
if word in new:
yield (word, new)
else:
time.sleep(0.5)
fn = 'test.py'
words = ['word']
for hit_word, hit_sentence in watch(fn, words):
print "Found %r in line: %r" % (hit_word, hit_sentence)
Это решение с readline
работает, если вы знаете, что ваши данные будут отображаться в строках.
Если данные представляют собой какой-то поток, вам нужен буфер, который больше, чем самый большой word
, который вы ищете, и сначала заполните его. Это немного усложняется...
def tail(f):
f.seek(0, 2)
while True:
line = f.readline()
if not line:
time.sleep(0.1)
continue
yield line
def process_matches(matchtext):
while True:
line = (yield)
if matchtext in line:
do_something_useful() # email alert, etc.
list_of_matches = ['ERROR', 'CRITICAL']
matches = [process_matches(string_match) for string_match in list_of_matches]
for m in matches: # prime matches
m.next()
while True:
auditlog = tail( open(log_file_to_monitor) )
for line in auditlog:
for m in matches:
m.send(line)
Я использую это для мониторинга файлов журнала. В полной реализации я сохраняю list_of_matches в файле конфигурации, поэтому его можно использовать для нескольких целей. В моем списке улучшений есть поддержка регулярного выражения, а не простое совпадение.
вы можете использовать pytailf: Простая оболочка python tail -f
from tailf import tailf
for line in tailf("myfile.log"):
print line
Вы можете использовать select для опроса для нового содержимого в файле.
def tail(filename, bufsize = 1024):
fds = [ os.open(filename, os.O_RDONLY) ]
while True:
reads, _, _ = select.select(fds, [], [])
if 0 < len(reads):
yield os.read(reads[0], bufsize)
EDIT: в качестве комментариев ниже примечания O_NONBLOCK
не работает для файлов на диске. Это все равно поможет, если кто-нибудь еще придет посмотреть на хвост данных, поступающих из сокета или именованного канала или другого процесса, но он не отвечает на фактический вопрос, который был задан. Первоначальный ответ остается ниже для потомков. (Вызов хвоста и grep будет работать, но в любом случае это не ответ.)
Либо откройте файл с помощью O_NONBLOCK
и используйте select
для опроса для доступности чтения, а затем read
для чтения новых данных и строковых методов для фильтрации строк в конце файла... или просто используйте модуль subprocess
, и пусть tail
и grep
выполняют для вас работу так же, как и в оболочке.
Похоже, есть пакет для этого: https://github.com/kasun/python-tail
Если вы не можете ограничить проблему работой для чтения на основе строки, вам нужно прибегнуть к блокам.
Это должно работать:
import sys
needle = "needle"
blocks = []
inf = sys.stdin
if len(sys.argv) == 2:
inf = open(sys.argv[1])
while True:
block = inf.read()
blocks.append(block)
if len(blocks) >= 2:
data = "".join((blocks[-2], blocks[-1]))
else:
data = blocks[-1]
# attention, this needs to be changed if you are interested
# in *all* matches separately, not if there was any match ata all
if needle in data:
print "found"
blocks = []
blocks[:-2] = []
if block == "":
break
Задача состоит в том, чтобы обеспечить совпадение иглы, даже если она разделена двумя границами блоков.
Если вам просто нужно простое решение Python 3 для обработки строк текстового файла по мере их написания, и вам не нужна поддержка Windows, это сработало для меня:
import subprocess
def tailf(filename):
#returns lines from a file, starting from the beginning
command = "tail -n +1 -F " + filename
p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, universal_newlines=True)
for line in p.stdout:
yield line
for line in tailf("logfile"):
#do stuff
Он блокирует ожидание записи новых строк, поэтому это не подходит для асинхронного использования без каких-либо изменений.
Насколько мне известно, в списке функций Python нет эквивалента "tail". Решение было бы использовать tell() (получить размер файла) и read() для разработки конечных строк.
Это сообщение в блоге (не я) имеет функцию, написанную, выглядит подходящей для меня! http://www.manugarg.com/2007/04/real-tailing-in-python.html
Вы можете использовать collections.deque
для реализации хвоста.
Из http://docs.python.org/library/collections.html#deque-recipes...
def tail(filename, n=10):
'Return the last n lines of a file'
return deque(open(filename), n)
Конечно, это читает все содержимое файла, но это аккуратный и сложный способ реализации хвоста.