Как просмотреть файл на предмет изменений?



У меня есть файл журнала, который записывается другим процессом, который я хочу наблюдать за изменениями. Каждый раз, когда происходит изменение, я хотел бы прочитать новые данные, чтобы сделать некоторую обработку на нем.



каков наилучший способ сделать это? Я надеялся, что там будет какой-то крючок из библиотеки PyWin32. Я нашел win32file.FindNextChangeNotification функция, но не знаю, как попросить его посмотреть конкретный файл.



Если кто-то сделал что-то подобное, я был бы очень благодарен услышать как...



[Edit] Я должен был упомянуть, что я был после решения, которое не требует опроса.



[Edit] проклятие! Кажется, это не работает на подключенном сетевом диске. Я предполагаю, что windows не "слышит" никаких обновлений файла, как это происходит на локальном диске.

735   20  

20 ответов:

вы уже посмотрели документацию, доступную на http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html? Если вам нужно только работать под Windows, второй пример кажется именно тем, что вы хотите (если вы обмениваете путь к каталогу с одним из файлов, которые вы хотите посмотреть).

в противном случае опрос, вероятно, будет единственным действительно независимым от платформы вариантом.

Примечание: Я не пробовал любое из этих решений.

вы пробовали использовать сторож?

библиотека API Python и утилиты оболочки для мониторинга событий файловой системы.

мониторинг каталогов легко сделать с

  • кросс-платформенный API.
  • инструмент оболочки для выполнения команд в ответ на изменения каталога.

начните быстро с простого примера в краткое руководство...

Если опрос достаточно хорош для вас, я просто посмотрю, изменится ли статистика файла "измененное время". Чтобы прочитать его:

os.stat(filename).st_mtime

(также обратите внимание, что решение Windows native change event работает не во всех случаях, например, на сетевых дисках.)

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...

Если вы хотите мультиплатформенное решение, то проверьте 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)

Он не должен работать на windows (может быть, с 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)

проверить pyinotify.

inotify заменяет dnotify (из более раннего ответа) в новых линуксах и позволяет мониторинг на уровне файлов, а не на уровне каталогов.

Ну после небольшого взлома скрипта Тима Голдена, у меня есть следующее, которое, кажется, работает довольно хорошо:

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'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's 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 )

вероятно, это может сделать с нагрузкой больше проверки ошибок, но для простого просмотра файла журнала и выполнения некоторой обработки на нем, прежде чем выплюнуть его на экран, это работает хорошо.

спасибо всем за Ваш вклад - отличная вещь!

самое простое решение для меня-использовать инструмент watchmedo сторожевого пса

от https://pypi.python.org/pypi/watchdog Теперь у меня есть процесс, который ищет файлы sql в каталоге и выполняет их при необходимости.

watchmedo shell-command \
--patterns="*.sql" \
--recursive \
--command='~/Desktop/load_files_into_mysql_database.sh' \
.

Регистрация мой ответ: до аналогичный вопрос. Вы можете попробовать тот же цикл в 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.

вот упрощенная версия кода Кендера, которая, похоже, делает тот же трюк и не импортирует весь файл:

# 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

Ну, так как вы используете Python, вы можете просто открыть файл и продолжать читать строки из него.

f = open('file.log')

если строка читается не пусто, вы обрабатываете его.

line = f.readline()
if line:
    // Do what you want with the line

вы можете пропустить, что это нормально, чтобы продолжать звонить readline в EOF. В этом случае он будет просто возвращать пустую строку. И когда что-то добавляется в файл журнала, чтение будет продолжаться с того места, где оно остановилось, как вам нужно.

Если вы ищете решение, которое использует события или определенную библиотеку, укажите это в своем вопросе. В противном случае, я думаю, что это решение просто отлично.

Это еще одна модификация скрипта Тима Голдана, который работает на linux и добавляет простой наблюдатель для изменения файла с помощью dict (file=>time).

использование: 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

Как вы можете видеть в статья Тима Голдена, указал на Хорст Гутмана, WIN32 является относительно сложным и наблюдает каталоги, а не один файл.

Я хотел бы предложить вам заглянуть в IronPython, который является .NET реализация python. С IronPython вы можете использовать все.NET функциональность-в том числе

System.IO.FileSystemWatcher

который обрабатывает отдельные файлы с простой событие взаимодействие.

для просмотра одного файла с опросом и минимальными зависимостями, вот полностью конкретизированный пример, основанный на ответе от 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

это пример проверки файла на наличие изменений. Тот, который может быть не лучшим способом сделать это, но это, конечно, короткий путь.

удобный инструмент для перезапуска приложения, когда изменения были внесены в источник. Я сделал это при игре с 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)

получайте удовольствие, заставляя электроны делать то, что вы хотите, чтобы они делали.

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's 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
                ...

вот пример, ориентированный на просмотр входных файлов, которые пишут не более одной строки в секунду, но обычно намного меньше. Цель состоит в том, чтобы добавить последнюю строку (самую последнюю запись) в указанный выходной файл. Я скопировал это из одного из моих проектов и просто удалил все ненужные строки. Вам придется заполнить или изменить недостающие символы.

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 один.

лучшее и самое простое решение-использовать pygtail: https://pypi.python.org/pypi/pygtail

from pygtail import Pygtail

while True:
    for line in Pygtail("some.log"):
        sys.stdout.write(line)

Я не знаю никакой конкретной функции Windows. Вы можете попробовать получить хэш MD5 файла каждую секунду / минуту / час (зависит от того, как быстро вам это нужно) и сравнить его с последним хэшем. Когда он отличается, вы знаете, что файл был изменен, и Вы читаете самые новые строки.

Я бы попробовал что-то вроде этого.

    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()

цикл проверяет, есть ли новая строка(ы) с момента последнего чтения файла - если есть, он читается и передается в

Comments

    Ничего не найдено.