Question

J'ai un fichier journal en cours d'écriture par un autre processus sur lequel je souhaite surveiller les modifications. À chaque changement, j'aimerais lire les nouvelles données et les traiter.

Quelle est la meilleure façon de faire cela? J'espérais qu'il y aurait une sorte de crochet de la bibliothèque PyWin32. J'ai trouvé la fonction win32file.FindNextChangeNotification mais je ne sais pas comment lui demander de regarder un fichier spécifique.

Si quelqu'un a fait quelque chose comme ça, je serais vraiment reconnaissant d'entendre comment ...

[Modifier] J'aurais dû mentionner que je cherchais une solution qui ne nécessite pas d'interrogation.

[Modifier] Curses! Il semble que cela ne fonctionne pas sur un lecteur réseau mappé. J'imagine que Windows n'entend pas les mises à jour du fichier comme sur un disque local.

Était-ce utile?

La solution

Avez-vous déjà consulté la documentation disponible sur http://timgolden.me.uk /python/win32_how_do_do/watch_directory_for_changes.html ? Si vous n’avez besoin que de Windows pour fonctionner, le deuxième exemple semble être exactement ce que vous voulez (si vous échangez le chemin du répertoire avec celui du fichier que vous voulez regarder).

Sinon, l'interrogation sera probablement la seule option réellement indépendante de la plate-forme.

Remarque: je n'ai encore essayé aucune de ces solutions.

Autres conseils

Avez-vous essayé d'utiliser Watchdog ?

  

Bibliothèque d'API Python et utilitaires shell permettant de surveiller les événements du système de fichiers.

     

La surveillance de répertoire simplifiée avec

     
      
  • Une API multiplate-forme.
  •   
  • Un outil de shell pour exécuter des commandes en réponse à des modifications de répertoire.
  •   
     

Commencez rapidement avec un exemple simple dans Démarrage rapide ...

Si le vote est suffisant pour vous, je regarderais simplement si le "temps modifié" fichier stat changements. Pour le lire:

os.stat(filename).st_mtime

(Notez également que la solution d'événements de changement natifs de Windows ne fonctionne pas dans toutes les circonstances, par exemple sur les lecteurs réseau.)

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

Si vous souhaitez une solution multiplateforme, consultez QFileSystemWatcher . Voici un exemple de code (non désinfecté):

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)

Cela ne devrait pas fonctionner sous Windows (peut-être avec cygwin?), mais pour les utilisateurs Unix, vous devez utiliser l'option "fcntl". appel système. Voici un exemple en Python. C'est généralement le même code si vous devez l'écrire en C (mêmes noms de fonctions)

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)

Découvrez pyinotify .

inotify remplace dnotify (d’une réponse précédente) dans les linux récents et autorise la surveillance au niveau fichier plutôt qu’au niveau répertoire.

Bien, après un peu de piratage du script de Tim Golden, j’ai le texte suivant qui semble fonctionner assez bien:

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 )

Cela pourrait probablement se faire avec une vérification plus lourde des erreurs de chargement, mais cela suffit pour regarder un fichier journal et le traiter avant de le cracher à l'écran.

Merci à tous pour votre contribution - une excellente chose!

La solution la plus simple pour moi consiste à utiliser l'outil de surveillance watchmedo

De https://pypi.python.org/pypi/watchdog J'ai maintenant un processus qui recherche les fichiers SQL dans un répertoire et les exécute si nécessaire.

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

Vérifiez ma réponse à une question similaire . Vous pouvez essayer la même boucle en Python. Cette page suggère:

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

Voir également la question tail () un fichier avec Python .

Eh bien, puisque vous utilisez Python, vous pouvez simplement ouvrir un fichier et continuer à en lire les lignes.

f = open('file.log')

Si la ligne lue est non vide , vous la traitez.

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

Il vous manque peut-être de continuer à appeler readline à l'EOF. Il va simplement continuer à retourner une chaîne vide dans ce cas. Et lorsque quelque chose est ajouté au fichier journal, la lecture reprend à l'endroit où elle s'est arrêtée, selon vos besoins.

Si vous recherchez une solution utilisant des événements ou une bibliothèque particulière, veuillez l'indiquer dans votre question. Sinon, je pense que cette solution est très bien.

Voici une version simplifiée du code de Kender qui semble faire la même chose et ne pas importer le fichier entier:

# 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

Pour regarder un seul fichier avec polling et dépendances minimes, voici un exemple complet, basé sur la réponse fournie par Deestan (ci-dessus):

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

Comme vous pouvez le voir dans l'article de Tim Golden , signalé par Horst Gutmann , WIN32 est relativement complexe et surveille les répertoires, pas un seul fichier.

J'aimerais vous suggérer de regarder IronPython , qui est un .NET implémentation python. Avec IronPython, vous pouvez utiliser toutes les fonctionnalités .NET , y compris

.
System.IO.FileSystemWatcher

Qui gère des fichiers uniques avec une simple interface Evénement .

C’est une autre modification du script de Tim Goldan qui s’exécute sur Linux et ajoute un simple observateur pour la modification de fichier à l’aide d’un dict (fichier = ).

usage: quel que soit le nom.py chemin_du_sauvegarde

#!/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

Voici un exemple de vérification des modifications d’un fichier. Ce n’est peut-être pas la meilleure façon de le faire, mais c’est un moyen court.

Outil pratique pour redémarrer l'application lorsque des modifications ont été apportées à la source. Je l’ai fait en jouant avec pygame pour que je puisse voir les effets se produire immédiatement après la sauvegarde du fichier.

Lorsqu’il est utilisé dans pygame, assurez-vous que les éléments de la boucle "while" sont placés dans votre boucle de jeu, c’est-à-dire update ou autre. Sinon, votre application sera bloquée dans une boucle infinie et votre jeu ne sera pas mis à jour.

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

Si vous voulez le code de redémarrage que j'ai trouvé sur le Web. C'est ici. (Pas pertinent pour la question, bien que cela puisse être utile)

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

Amusez-vous à faire en sorte que les électrons fassent ce que vous voulez.

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

Voici un exemple conçu pour regarder des fichiers d'entrée n'écrivant pas plus d'une ligne par seconde, mais généralement beaucoup moins. L'objectif est d'ajouter la dernière ligne (écriture la plus récente) au fichier de sortie spécifié. Je l'ai copié de l'un de mes projets et je viens de supprimer toutes les lignes non pertinentes. Vous devrez remplir ou modifier les symboles manquants.

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

Bien sûr, la classe QMainWindow englobante n’est pas strictement requise, c’est-à-dire. vous pouvez utiliser QFileSystemWatcher seul.

La solution la plus simple et la plus simple consiste à utiliser pygtail:     https://pypi.python.org/pypi/pygtail

from pygtail import Pygtail

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

Vous pouvez également utiliser une bibliothèque simple appelée repyt , voici un exemple:

repyt ./app.py

Il semble que personne n'ait posté fswatch . C'est un observateur de système de fichiers multiplateforme. Installez-le, lancez-le et suivez les instructions.

Je l'ai utilisé avec les programmes python et golang et cela fonctionne.

related @ 4Oh4 solution un changement en douceur pour une liste de fichiers à regarder;

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

Je ne connais aucune fonction spécifique de Windows. Vous pouvez essayer d'obtenir le hachage MD5 du fichier toutes les secondes / minutes / heures (en fonction de la rapidité avec laquelle vous en avez besoin) et le comparer au dernier hachage. Quand cela diffère, vous savez que le fichier a été modifié et que vous lisez les dernières lignes.

Je voudrais essayer quelque chose comme ça.

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

La boucle vérifie s’il existe une ou plusieurs nouvelles lignes depuis la dernière lecture du fichier. Le cas échéant, elle est lue et transmise à la fonction functionThatAnalisesTheLine . Sinon, le script attend 1 seconde et relance le processus.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top