Как просмотреть файл для изменений?
У меня есть файл журнала, написанный другим процессом, который я хочу наблюдать за изменениями. Каждый раз, когда происходит изменение, я хотел бы прочитать новые данные, чтобы выполнить некоторую обработку.
Какой лучший способ сделать это? Я надеялся, что в библиотеке PyWin32 появится какой-то крючок. Я нашел функцию win32file.FindNextChangeNotification
, но понятия не имею, как попросить ее посмотреть конкретный файл.
Если бы кто-нибудь сделал что-нибудь подобное, я был бы очень благодарен, чтобы услышать, как...
[Изменить] Я должен был упомянуть, что я был после решения, которое не требует опроса.
[Изменить] Curses! Кажется, что это не работает над подключенным сетевым диском. Я предполагаю, что окна не "слышат" никаких обновлений для файла так, как это делается на локальном диске.
Ответы
Ответ 1
Вы уже просмотрели документацию, доступную на http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html? Если вам нужно только работать под Windows, второй пример, похоже, будет именно тем, что вы хотите (если вы обмениваетесь контуром каталога с одним из файлов, который вы хотите посмотреть).
В противном случае, опрос, вероятно, будет единственным действительно независимым от платформы вариантом.
Примечание. Я не пробовал ни одно из этих решений.
Ответ 2
Вы пытались использовать Watchdog?
Библиотека API Python и утилиты оболочки для мониторинга событий файловой системы.
Мониторинг каталогов стал проще с помощью
- Кросс-платформенный API.
- Инструмент оболочки для запуска команд в ответ на изменения каталога.
Начните быстро с простого примера в Quickstart...
Ответ 3
Если опрос достаточно хорош для вас, я просто посмотрю, изменится ли файл изменения "измененного времени". Чтобы прочитать его:
os.stat(filename).st_mtime
(Также обратите внимание, что исходное изменение событий Windows не работает при любых обстоятельствах, например, на сетевых дисках.)
import os
class Monkey(object):
def __init__(self):
self._cached_stamp = 0
self.filename = '/path/to/file'
def ook(self):
stamp = os.stat(self.filename).st_mtime
if stamp != self._cached_stamp:
self._cached_stamp = stamp
# File has changed, so do something...
Ответ 4
Если вы хотите использовать многоплатформенное решение, проверьте QFileSystemWatcher.
Вот пример кода (не дезинфицированный):
from PyQt4 import QtCore
@QtCore.pyqtSlot(str)
def directory_changed(path):
print('Directory Changed!!!')
@QtCore.pyqtSlot(str)
def file_changed(path):
print('File Changed!!!')
fs_watcher = QtCore.QFileSystemWatcher(['/path/to/files_1', '/path/to/files_2', '/path/to/files_3'])
fs_watcher.connect(fs_watcher, QtCore.SIGNAL('directoryChanged(QString)'), directory_changed)
fs_watcher.connect(fs_watcher, QtCore.SIGNAL('fileChanged(QString)'), file_changed)
Ответ 5
Он не должен работать на окнах (возможно, с cygwin?), но для пользователя unix вы должны использовать системный вызов "fcntl". Вот пример в Python. Это в основном тот же код, если вам нужно записать его в C (те же имена функций)
import time
import fcntl
import os
import signal
FNAME = "/HOME/TOTO/FILETOWATCH"
def handler(signum, frame):
print "File %s modified" % (FNAME,)
signal.signal(signal.SIGIO, handler)
fd = os.open(FNAME, os.O_RDONLY)
fcntl.fcntl(fd, fcntl.F_SETSIG, 0)
fcntl.fcntl(fd, fcntl.F_NOTIFY,
fcntl.DN_MODIFY | fcntl.DN_CREATE | fcntl.DN_MULTISHOT)
while True:
time.sleep(10000)
Ответ 6
Отъезд pyinotify.
inotify заменяет dnotify (от более раннего ответа) в новых linux и позволяет контролировать уровень файлов, а не на уровне каталогов.
Ответ 7
Ну, после немного взлома Тима Голден script, у меня есть следующее, которое, кажется, работает достаточно хорошо:
import os
import win32file
import win32con
path_to_watch = "." # look at the current directory
file_to_watch = "test.txt" # look for changes to a file called test.txt
def ProcessNewData( newData ):
print "Text added: %s"%newData
# Set up the bits we'll need for output
ACTIONS = {
1 : "Created",
2 : "Deleted",
3 : "Updated",
4 : "Renamed from something",
5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001
hDir = win32file.CreateFile (
path_to_watch,
FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS,
None
)
# Open the file we're interested in
a = open(file_to_watch, "r")
# Throw away any exising log data
a.read()
# Wait for new data and call ProcessNewData for each new chunk that written
while 1:
# Wait for a change to occur
results = win32file.ReadDirectoryChangesW (
hDir,
1024,
False,
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
None,
None
)
# For each change, check to see if it updating the file we're interested in
for action, file in results:
full_filename = os.path.join (path_to_watch, file)
#print file, ACTIONS.get (action, "Unknown")
if file == file_to_watch:
newText = a.read()
if newText != "":
ProcessNewData( newText )
Вероятно, это может произойти с проверкой ошибок при загрузке, но для простого просмотра файла журнала и выполнения некоторой обработки на нем, прежде чем выплевывать его на экран, это хорошо работает.
Спасибо всем за ваш вклад - отличный материал!
Ответ 8
Самое простое решение для меня - использовать watchdog tool watchmedo
От https://pypi.python.org/pypi/watchdog Теперь у меня есть процесс, который ищет файлы sql в каталоге и при необходимости выполняет их.
watchmedo shell-command \
--patterns="*.sql" \
--recursive \
--command='~/Desktop/load_files_into_mysql_database.sh' \
.
Ответ 9
Проверьте мой ответ на аналогичный вопрос. Вы можете попробовать тот же цикл в Python. Эта страница предлагает:
import time
while 1:
where = file.tell()
line = file.readline()
if not line:
time.sleep(1)
file.seek(where)
else:
print line, # already has newline
Также смотрите вопрос tail() файл с Python.
Ответ 10
Хорошо, поскольку вы используете Python, вы можете просто открыть файл и сохранить строки чтения.
f = open('file.log')
Если прочитанная строка не пустой, вы ее обрабатываете.
line = f.readline()
if line:
// Do what you want with the line
Возможно, вам не хватает того, что нормально поддерживать вызов readline
в EOF. В этом случае он просто вернет пустую строку. И когда что-то добавляется в файл журнала, чтение будет продолжаться с того места, где оно остановилось, как вам нужно.
Если вы ищете решение, которое использует события или конкретную библиотеку, укажите это в своем вопросе. В противном случае, я думаю, это решение просто отлично.
Ответ 11
Вот упрощенная версия кода Kender, которая, похоже, выполняет тот же трюк и не импортирует весь файл:
# Check file for new data.
import time
f = open(r'c:\temp\test.txt', 'r')
while True:
line = f.readline()
if not line:
time.sleep(1)
print 'Nothing New'
else:
print 'Call Function: ', line
Ответ 12
Для просмотра одного файла с опросом и минимальных зависимостей здесь приведен полный пример, основанный на ответе Deestan (вверху):
import os
import sys
import time
class Watcher(object):
running = True
refresh_delay_secs = 1
# Constructor
def __init__(self, watch_file, call_func_on_change=None, *args, **kwargs):
self._cached_stamp = 0
self.filename = watch_file
self.call_func_on_change = call_func_on_change
self.args = args
self.kwargs = kwargs
# Look for changes
def look(self):
stamp = os.stat(self.filename).st_mtime
if stamp != self._cached_stamp:
self._cached_stamp = stamp
# File has changed, so do something...
print('File changed')
if self.call_func_on_change is not None:
self.call_func_on_change(*self.args, **self.kwargs)
# Keep watching in a loop
def watch(self):
while self.running:
try:
# Look for changes
time.sleep(self.refresh_delay_secs)
self.look()
except KeyboardInterrupt:
print('\nDone')
break
except FileNotFoundError:
# Action on file not found
pass
except:
print('Unhandled error: %s' % sys.exc_info()[0])
# Call this function each time a change happens
def custom_action(text):
print(text)
watch_file = 'my_file.txt'
# watcher = Watcher(watch_file) # simple
watcher = Watcher(watch_file, custom_action, text='yes, changed') # also call custom action function
watcher.watch() # start the watch going
Ответ 13
Это еще одна модификация Tim Goldan script, которая работает на Linux и добавляет простой наблюдатель для изменения файла с помощью dict (file = > time).
use: whateverName.py path_to_dir_to_watch
#!/usr/bin/env python
import os, sys, time
def files_to_timestamp(path):
files = [os.path.join(path, f) for f in os.listdir(path)]
return dict ([(f, os.path.getmtime(f)) for f in files])
if __name__ == "__main__":
path_to_watch = sys.argv[1]
print "Watching ", path_to_watch
before = files_to_timestamp(path_to_watch)
while 1:
time.sleep (2)
after = files_to_timestamp(path_to_watch)
added = [f for f in after.keys() if not f in before.keys()]
removed = [f for f in before.keys() if not f in after.keys()]
modified = []
for f in before.keys():
if not f in removed:
if os.path.getmtime(f) != before.get(f):
modified.append(f)
if added: print "Added: ", ", ".join(added)
if removed: print "Removed: ", ", ".join(removed)
if modified: print "Modified ", ", ".join(modified)
before = after
Ответ 14
Как вы можете видеть в Тим Золотая статья, указана Хорстом Гутманном, WIN32 относительно сложна и смотрит каталоги, а не один файл.
Я хотел бы предложить вам изучить IronPython, который является реализацией .NET python.
С IronPython вы можете использовать все функциональные возможности .NET - в том числе
System.IO.FileSystemWatcher
Он обрабатывает отдельные файлы с помощью простого интерфейса Event.
Ответ 15
ACTIONS = {
1 : "Created",
2 : "Deleted",
3 : "Updated",
4 : "Renamed from something",
5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001
class myThread (threading.Thread):
def __init__(self, threadID, fileName, directory, origin):
threading.Thread.__init__(self)
self.threadID = threadID
self.fileName = fileName
self.daemon = True
self.dir = directory
self.originalFile = origin
def run(self):
startMonitor(self.fileName, self.dir, self.originalFile)
def startMonitor(fileMonitoring,dirPath,originalFile):
hDir = win32file.CreateFile (
dirPath,
FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS,
None
)
# Wait for new data and call ProcessNewData for each new chunk that's
# written
while 1:
# Wait for a change to occur
results = win32file.ReadDirectoryChangesW (
hDir,
1024,
False,
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
None,
None
)
# For each change, check to see if it updating the file we're
# interested in
for action, file_M in results:
full_filename = os.path.join (dirPath, file_M)
#print file, ACTIONS.get (action, "Unknown")
if len(full_filename) == len(fileMonitoring) and action == 3:
#copy to main file
...
Ответ 16
Это пример проверки файла для изменений. Один из них, возможно, не лучший способ сделать это, но это, безусловно, короткий путь.
Удобный инструмент для перезапуска приложения, когда были внесены изменения в источник. Я сделал это, играя с pygame, поэтому я вижу, что эффекты происходят сразу после сохранения файла.
При использовании в pygame убедитесь, что материал в цикле "while" помещен в ваш игровой цикл aka update или что-то еще. В противном случае ваше приложение застрянет в бесконечном цикле, и вы не увидите обновление своей игры.
file_size_stored = os.stat('neuron.py').st_size
while True:
try:
file_size_current = os.stat('neuron.py').st_size
if file_size_stored != file_size_current:
restart_program()
except:
pass
Если вам нужен код перезагрузки, который я нашел в Интернете. Вот. (Не относится к вопросу, хотя это может пригодиться)
def restart_program(): #restart application
python = sys.executable
os.execl(python, python, * sys.argv)
Получайте удовольствие, делая электроны, делая то, что вы хотите, чтобы они делали.
Ответ 17
Здесь пример, ориентированный на просмотр входных файлов, которые пишут не более одной строки в секунду, но обычно намного меньше. Цель состоит в том, чтобы добавить последнюю строку (самую последнюю запись) в указанный выходной файл. Я скопировал это из одного из моих проектов и просто удалил все ненужные строки. Вам нужно будет заполнить или изменить отсутствующие символы.
from PyQt5.QtCore import QFileSystemWatcher, QSettings, QThread
from ui_main_window import Ui_MainWindow # Qt Creator gen'd
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
Ui_MainWindow.__init__(self)
self._fileWatcher = QFileSystemWatcher()
self._fileWatcher.fileChanged.connect(self.fileChanged)
def fileChanged(self, filepath):
QThread.msleep(300) # Reqd on some machines, give chance for write to complete
# ^^ About to test this, may need more sophisticated solution
with open(filepath) as file:
lastLine = list(file)[-1]
destPath = self._filemap[filepath]['dest file']
with open(destPath, 'a') as out_file: # a= append
out_file.writelines([lastLine])
Конечно, всеобъемлющий класс QMainWindow строго не требуется, т.е. вы можете использовать только QFileSystemWatcher.
Ответ 18
Лучшее и самое простое решение - использовать pygtail: https://pypi.python.org/pypi/pygtail
from pygtail import Pygtail
while True:
for line in Pygtail("some.log"):
sys.stdout.write(line)
Ответ 19
Вы также можете использовать простую библиотеку под названием repyt, вот пример:
repyt ./app.py
Ответ 20
Кажется, никто не опубликовал fswatch. Это кроссплатформенный наблюдатель файловой системы. Просто установите его, запустите и следуйте инструкциям.
Я использовал его с программами Python и Golang, и он просто работает.
Ответ 21
related @4Oh4 solution - плавное изменение списка файлов для просмотра;
import os
import sys
import time
class Watcher(object):
running = True
refresh_delay_secs = 1
# Constructor
def __init__(self, watch_files, call_func_on_change=None, *args, **kwargs):
self._cached_stamp = 0
self._cached_stamp_files = {}
self.filenames = watch_files
self.call_func_on_change = call_func_on_change
self.args = args
self.kwargs = kwargs
# Look for changes
def look(self):
for file in self.filenames:
stamp = os.stat(file).st_mtime
if not file in self._cached_stamp_files:
self._cached_stamp_files[file] = 0
if stamp != self._cached_stamp_files[file]:
self._cached_stamp_files[file] = stamp
# File has changed, so do something...
file_to_read = open(file, 'r')
value = file_to_read.read()
print("value from file", value)
file_to_read.seek(0)
if self.call_func_on_change is not None:
self.call_func_on_change(*self.args, **self.kwargs)
# Keep watching in a loop
def watch(self):
while self.running:
try:
# Look for changes
time.sleep(self.refresh_delay_secs)
self.look()
except KeyboardInterrupt:
print('\nDone')
break
except FileNotFoundError:
# Action on file not found
pass
except Exception as e:
print(e)
print('Unhandled error: %s' % sys.exc_info()[0])
# Call this function each time a change happens
def custom_action(text):
print(text)
# pass
watch_files = ['/Users/mexekanez/my_file.txt', '/Users/mexekanez/my_file1.txt']
# watcher = Watcher(watch_file) # simple
if __name__ == "__main__":
watcher = Watcher(watch_files, custom_action, text='yes, changed') # also call custom action function
watcher.watch() # start the watch going
Ответ 22
Я не знаю никакой конкретной функции Windows. Вы можете попробовать получить хеш MD5 файла каждую секунду/минуту/час (зависит от того, насколько быстро он вам нужен) и сравнить его с последним хэшем. Когда он отличается, вы знаете, что файл был изменен, и вы зачитали новейшие строки.
Ответ 23
Я бы попробовал что-то вроде этого.
try:
f = open(filePath)
except IOError:
print "No such file: %s" % filePath
raw_input("Press Enter to close window")
try:
lines = f.readlines()
while True:
line = f.readline()
try:
if not line:
time.sleep(1)
else:
functionThatAnalisesTheLine(line)
except Exception, e:
# handle the exception somehow (for example, log the trace) and raise the same exception again
raw_input("Press Enter to close window")
raise e
finally:
f.close()
Цикл проверяет, есть ли новая строка (строки), поскольку последний файл был прочитан - если есть, он читается и передается функции functionThatAnalisesTheLine
. Если нет, script ждет 1 секунду и повторит процесс.