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

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

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

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

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

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

Ответы [ 22 ]

260 голосов
/ 14 января 2011

Вы пытались использовать Watchdog ?

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

Мониторинг каталогов стал проще с

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

Начните быстро с простого примера в Быстрый старт ...

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

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

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...
71 голосов
/ 08 октября 2008

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

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

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

49 голосов
/ 17 марта 2011

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

Он не должен работать на 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)
21 голосов
/ 13 июня 2010

Выезд pyinotify .

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

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

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

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 )

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

Спасибо всем за ваш вклад - отличный материал!

7 голосов
/ 25 июня 2014

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

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

watchmedo shell-command \
--patterns="*.sql" \
--recursive \
--command='~/Desktop/load_files_into_mysql_database.sh' \
.
6 голосов
/ 08 октября 2008

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

6 голосов
/ 08 декабря 2009

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

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