it-swarm.com.de

Wie kann ich eine Datei auf Änderungen überprüfen?

Ich habe eine Protokolldatei, die von einem anderen Prozess geschrieben wird, den ich auf Änderungen überwachen möchte. Bei jeder Änderung möchte ich die neuen Daten einlesen, um sie zu bearbeiten.

Was ist der beste Weg dies zu tun? Ich hatte gehofft, dass es eine Art Hook aus der PyWin32-Bibliothek gibt. Ich habe die win32file.FindNextChangeNotification-Funktion gefunden, habe aber keine Ahnung, wie ich sie bitten soll, eine bestimmte Datei anzusehen.

Wenn jemand so etwas getan hat, wäre ich wirklich dankbar zu hören, wie ...

[Bearbeiten] Ich hätte sagen sollen, dass ich nach einer Lösung suchte, die keine Abfrage erfordert.

[Bearbeiten] Curses! Es scheint, dass dies nicht über ein zugeordnetes Netzlaufwerk funktioniert. Ich vermute, Windows "hört" keine Aktualisierungen der Datei wie auf einer lokalen Festplatte.

276
Jon Cage

Haben Sie bereits die verfügbare Dokumentation auf http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html gesehen? Wenn Sie es nur benötigen, um unter Windows zu funktionieren, scheint das zweite Beispiel genau das zu sein, was Sie möchten (wenn Sie den Pfad des Verzeichnisses mit dem Pfad der Datei austauschen, die Sie ansehen möchten). 

Andernfalls ist die Abfrage wahrscheinlich die einzige wirklich plattformunabhängige Option.

Hinweis: Ich habe keine dieser Lösungen ausprobiert.

67
Horst Gutmann

Haben Sie versucht, Watchdog zu verwenden?

Python-API-Bibliothek und Shell-Dienstprogramme zum Überwachen von Dateisystemereignissen.

Verzeichnisüberwachung leicht gemacht mit

  • Eine plattformübergreifende API.
  • Ein Shell-Tool zum Ausführen von Befehlen als Reaktion auf Verzeichnisänderungen.

Beginnen Sie schnell mit einem einfachen Beispiel in Quickstart ...

251
simao

Wenn die Abfrage für Sie gut genug ist, würde ich nur beobachten, ob sich der Status der geänderten Zeit ändert. Um es zu lesen:

os.stat(filename).st_mtime

(Beachten Sie auch, dass die native Windows-Lösung für Änderungsereignisse nicht unter allen Umständen funktioniert, beispielsweise auf Netzlaufwerken.)

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

Wenn Sie eine Multiplattform-Lösung wünschen, überprüfen Sie QFileSystemWatcher . Hier ein Beispielcode (nicht bereinigt):

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)
48
hipersayan_x

Es sollte nicht unter Windows funktionieren (vielleicht mit Cygwin?), Aber für Unix-Benutzer sollten Sie den Systemaufruf "fcntl" verwenden. Hier ist ein Beispiel in Python. Es ist meist derselbe Code, wenn Sie ihn in C schreiben müssen (gleiche Funktionsnamen).

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)
24
Maxime

Check out pyinotify .

inotify ersetzt dnotify (aus einer früheren Antwort) in neueren Linux-Versionen und ermöglicht die Überwachung auf Dateiebene statt auf Verzeichnisebene.

21
Michael Palmer

Nach ein bisschen Hacken von Tim Goldens Skript habe ich folgendes, was ziemlich gut zu funktionieren scheint:

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 )

Dies könnte mit einer stärkeren Fehlerprüfung zusammenhängen, aber wenn Sie einfach eine Protokolldatei ansehen und etwas bearbeiten, bevor Sie sie auf den Bildschirm spucken, funktioniert dies gut.

Vielen Dank an alle für Ihre Beiträge - großartiges Zeug!

14
Jon Cage

Die einfachste Lösung für mich ist das Watchdog-Tool watchmedo

Von https://pypi.python.org/pypi/watchdog habe ich nun einen Prozess, der die SQL-Dateien in einem Verzeichnis nachschlägt und sie bei Bedarf ausführt. 

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

Hier ist eine vereinfachte Version von Kenders Code, die den gleichen Trick auszuführen scheint und nicht die gesamte Datei importiert:

# 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
6
AlaXul

Überprüfen Sie meine Antwort auf eine ähnliche Frage . Sie können die gleiche Schleife in Python versuchen. Diese Seite schlägt vor:

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

Siehe auch die Frage tail () einer Datei mit Python .

6
Bruno De Fraine

Nun, da Sie Python verwenden, können Sie einfach eine Datei öffnen und Zeilen daraus lesen.

f = open('file.log')

Wenn die gelesene Zeile nicht leer ist, verarbeiten Sie sie.

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

Möglicherweise fehlt Ihnen, dass Sie in der EOF ständig readline aufrufen können. In diesem Fall wird nur eine leere Zeichenfolge zurückgegeben. Und wenn etwas an die Protokolldatei angehängt wird, wird das Lesen an der Stelle fortgesetzt, an der es angehalten wurde.

Wenn Sie nach einer Lösung suchen, die Ereignisse oder eine bestimmte Bibliothek verwendet, geben Sie dies bitte in Ihrer Frage an. Ansonsten finde ich diese Lösung gut.

5
seuvitor

Dies ist eine weitere Modifikation des Tim Goldan-Skripts, das unter Linux ausgeführt wird und einen einfachen Watcher für die Dateiverschiebung mit einem Dict (Datei => Zeit) hinzufügt.

usage: whateverName.py pfad_zur_dir_zur_uhr

#!/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 __== "__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
4
ronedg

Wie Sie in Tim Goldens Artikel , von Horst Gutmann gezeigt, sehen können, ist WIN32 relativ komplex und überwacht Verzeichnisse, keine einzelne Datei.

Ich möchte vorschlagen, dass Sie in IronPython nachsehen, einer Python-Implementierung von .NET. Mit IronPython können Sie die gesamte Funktionalität von .NET verwenden - einschließlich

System.IO.FileSystemWatcher

Die behandelt einzelne Dateien mit einer einfachen Event - Schnittstelle.

3
gimel

Um eine einzelne Datei mit Abfragen und minimalen Abhängigkeiten zu betrachten, ist hier ein ausführliches Beispiel, basierend auf der Antwort von Deestan (oben):

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
3
4Oh4

Dies ist ein Beispiel für das Überprüfen einer Datei auf Änderungen. Einer, der vielleicht nicht der beste Weg ist, aber es ist sicher ein kurzer Weg.

Praktisches Werkzeug zum Neustarten der Anwendung, wenn Änderungen an der Quelle vorgenommen wurden. Ich habe dies beim Spielen mit Pygame gemacht, so dass ich Effekte sofort nach dem Speichern der Datei sehen kann.

Wenn Sie in Pygame verwendet werden, stellen Sie sicher, dass das Zeug in der While-Schleife in Ihrer Spielschleife (Update oder was auch immer) platziert ist. Andernfalls bleibt Ihre Anwendung in einer Endlosschleife stecken und Sie sehen keine Aktualisierung Ihres Spiels.

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

Falls Sie den Neustartcode wünschen, den ich im Web gefunden habe. Hier ist es. (Nicht relevant für die Frage, obwohl es nützlich sein könnte)

def restart_program(): #restart application
    python = sys.executable
    os.execl(python, python, * sys.argv)

Viel Spaß dabei, Elektronen so zu machen, wie Sie möchten.

1
Bassim Huis

Hier ist ein Beispiel, das auf das Betrachten von Eingabedateien ausgerichtet ist, die nicht mehr als eine Zeile pro Sekunde schreiben, normalerweise aber viel weniger. Ziel ist es, die letzte Zeile (zuletzt geschrieben) an die angegebene Ausgabedatei anzuhängen. Ich habe dies aus einem meiner Projekte kopiert und einfach alle irrelevanten Zeilen gelöscht. Sie müssen die fehlenden Symbole ausfüllen oder ändern. 

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

Natürlich ist die umfassende QMainWindow-Klasse nicht unbedingt erforderlich, d. Sie können QFileSystemWatcher alleine verwenden. 

1
user1277936
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
                ...
1
imp

Scheint, dass niemand fswatch geschrieben hat. Es ist ein plattformübergreifender Dateisystem-Watcher. Einfach installieren, ausführen und den Anweisungen folgen.

Ich habe es mit Python- und Golang-Programmen verwendet und es funktioniert einfach.

0
Gus

Sie können auch eine einfache Bibliothek mit dem Namen repyt verwenden. Hier ein Beispiel:

repyt ./app.py
0
Rafal Enden

Die beste und einfachste Lösung ist die Verwendung von pygtail: https://pypi.python.org/pypi/pygtail

from pygtail import Pygtail

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

related @ 4Oh4-Lösung eine reibungslose Änderung für eine Liste von Dateien zu sehen;

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 __== "__main__":
    watcher = Watcher(watch_files, custom_action, text='yes, changed')  # also call custom action function
    watcher.watch()  # start the watch going
0
mexekanez