Как посмотреть файл на предмет изменений? - PullRequest
291 голосов
/ 08 октября 2008

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

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

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

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

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

Ответы [ 22 ]

6 голосов
/ 08 октября 2008

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

f = open('file.log')

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

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

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

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

4 голосов
/ 27 февраля 2018

Для просмотра отдельного файла с опросом и минимальными зависимостями, вот полностью выделенный пример, основанный на ответе Дестан (выше):

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
4 голосов
/ 25 февраля 2013

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

использование: whatName.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
3 голосов
/ 08 октября 2008

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

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

System.IO.FileSystemWatcher

, который обрабатывает отдельные файлы с помощью простого события интерфейса.

1 голос
/ 21 сентября 2016

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

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 в одиночку.

1 голос
/ 20 апреля 2014

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

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

При использовании в pygame убедитесь, что материал в цикле while размещен в вашем игровом цикле, то есть в обновлении или в любом другом месте. В противном случае ваше приложение застрянет в бесконечном цикле, и вы не увидите обновления вашей игры.

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)

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

1 голос
/ 22 сентября 2013
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
                ...
0 голосов
/ 31 января 2018

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

from pygtail import Pygtail

while True:
    for line in Pygtail("some.log"):
        sys.stdout.write(line)
0 голосов
/ 08 октября 2008

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

    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. Если нет, скрипт ждет 1 секунду и повторяет процесс.

0 голосов
/ 19 ноября 2018

Вы также можете использовать простую библиотеку под названием repyt , вот пример:

repyt ./app.py
...