Прочитать новую строку с pynotify
Я пытаюсь отобразить новую строку, которая была добавлена в файл. Итак, представьте, что у меня есть sample_file.txt
:
1. line 1
2. line 2
3. line 3
Я хочу проверить, получил ли этот файл новую строку, и затем отобразить эту строку (без повторной печати всего файла)
#!/usr/bin/python
import os
import pyinotify
from datetime import datetime
import time
PATH = os.path.join(os.path.expanduser('~/'), 'sample_file.txt')
m = pyinotify.WatchManager()
m.add_watch(PATH, pyinotify.ALL_EVENTS, rec=True)
notifier = pyinotify.Notifier(m, pyinotify.ProcessEvent(), 0, 0, 100)
f = open(PATH)
for line in f.readlines():
print line
while True:
time.sleep(5)
try:
if notifier.check_events():
# RIGHT HERE:
# HOW TO DO SOMETHING LIKE
# f.last() ???
print f.next()
else:
print 'waiting for a line....'
except KeyboardInterrupt:
notifier.stop()
break
Я думал о том, чтобы прочитать все строки где-то перед циклом while, а затем вывести следующую строку, но что-то не так в моем коде, и он проверяет f.next()
сразу после того, как дело доходит до петли.
1 ответ
Я рассмотрю два вопроса:
- как реализовать
tail
в файле, - и как использовать
pyinotify
модуль.
Хвост по файлу
В вашем коде вам необходимо:
- попробуйте прочитать как можно больше полных строк, используя
read
или жеreadlines
, - перемотайте файл до начала последней незавершенной строки, пока вы не сможете распечатать его, используя
seek
,
Это переводит, например, в:
f = open(PATH)
for line in f.readlines():
print line[:-1]
while True:
time.sleep(5)
try:
line_start = f.tell()
new_lines = f.read()
last_n = new_lines.rfind('\n')
if last_n >= 0:
# We got at least one full line
line_start += last_n + 1
print new_lines[:last_n]
else:
# No complete line yet
print 'no line'
f.seek(line_start)
except KeyboardInterrupt:
notifier.stop()
break
Вы можете найти больше примеров здесь, хотя некоторые не обращаются к дополнениям к файлу, которые не заканчиваются новой строкой:
- https://github.com/kasun/python-tail/blob/master/tail.py
- http://code.activestate.com/recipes/157035-tail-f-in-python/
И некоторые альтернативы здесь Как я могу подключить файл журнала в Python?
Петля пиинотификации
Вы также должны переместить свой код внутрь pyinotify
обработчики событий, как описано в документации.
check_events
возвращается True
если есть события, которые нужно обработать, но он на самом деле не обрабатывает события, поэтому сам по себе он всегда вернет True
пока события не были обработаны.
Кроме того, постарайтесь избежать while
/sleep
петли. Inotify добавляет возможность обрабатывать событие как можно быстрее, без ущерба для ресурсов. while
/sleep
петля будет менее реактивной.
Ниже приведены два первых метода из краткого учебника по pyinotify
,
1. Мониторинг бесконечно
Это предпочтительный метод, если у вас нет другого цикла событий, так как он будет наиболее реактивным:
PATH = os.path.join(os.path.expanduser('~/'), 'experiments', 'testfile')
class EventHandler(pyinotify.ProcessEvent):
def __init__(self, *args, **kwargs):
super(EventHandler, self).__init__(*args, **kwargs)
self.file = open(PATH)
self.position = 0
self.print_lines()
def process_IN_MODIFY(self, event):
print 'event received'
self.print_lines()
def print_lines(self):
new_lines = self.file.read()
last_n = new_lines.rfind('\n')
if last_n >= 0:
self.position += last_n + 1
print new_lines[:last_n]
else:
print 'no line'
self.file.seek(self.position)
wm = pyinotify.WatchManager()
handler = EventHandler()
notifier = pyinotify.Notifier(wm, handler)
wm.add_watch(PATH, pyinotify.IN_MODIFY, rec=True)
notifier.loop()
2. Периодический мониторинг
Если у вас уже есть цикл обработки, то это просто вопрос вызова process_events
периодически. EventHandler
класс такой же, как в методе 1, но теперь вместо вызова notifier.loop()
мы добавляем небольшой таймаут к уведомителю и реализуем наш собственный цикл обработки событий.
...
wm = pyinotify.WatchManager()
handler = EventHandler()
notifier = pyinotify.Notifier(wm, handler, timeout=10)
wm.add_watch(PATH, pyinotify.IN_MODIFY, rec=True)
while True:
# Do something unrelated to pyinotify
time.sleep(5)
notifier.process_events()
#loop in case more events appear while we are processing
while notifier.check_events():
notifier.read_events()
notifier.process_events()