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

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

Какой лучший способ сделать это? Я надеялся, что в библиотеке 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' \
.

Ответ 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 секунду и повторит процесс.