web-dev-qa-db-fra.com

Comment regarder un fichier pour des modifications?

J'ai un fichier journal en cours d'écriture par un autre processus sur lequel je souhaite surveiller les modifications. Chaque fois qu'un changement se produit, j'aimerais lire les nouvelles données afin de 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 ...

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

[Edit] 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.

276
Jon Cage

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, la scrutation sera probablement la seule option réellement indépendante de la plate-forme.

Note: Je n'ai essayé aucune de ces solutions.

67
Horst Gutmann

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 d'annuaire rendue facile avec

  • Une API multi-plateforme.
  • Un outil Shell pour exécuter des commandes en réponse aux changements de répertoire.

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

251
simao

Si l'interrogation vous convient, je regarderai simplement si la statistique du fichier "heure modifiée" change. Pour le lire:

os.stat(filename).st_mtime

(Notez également que la solution d’événement de modification native 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...
79
Deestan

Si vous voulez une solution multiplateforme, vérifiez 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)
48
hipersayan_x

Cela ne devrait pas fonctionner sous Windows (peut-être avec cygwin?), Mais pour les utilisateurs unix, vous devriez utiliser l'appel système "fcntl". Voici un exemple en Python. C'est essentiellement 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)
24
Maxime

Départ pyinotify .

inotify remplace dnotify (à partir d'une réponse précédente) dans les linux les plus récents et permet une surveillance au niveau fichier plutôt que répertoire.

21
Michael Palmer

Après un peu de piratage du script de Tim Golden, voici ce qui semble bien fonctionner:

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 chargée des erreurs de chargement, mais pour simplement regarder un fichier journal et effectuer un traitement dessus avant de le cracher à l'écran, cela fonctionne bien.

Merci à tous pour votre contribution - super chose!

14
Jon Cage

La solution la plus simple pour moi consiste à utiliser l'outil watchdog 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' \
.
7
redestructa

Voici une version simplifiée du code de Kender qui semble faire la même chose sans 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
6
AlaXul

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 aussi la question tail () un fichier avec Python .

6
Bruno De Fraine

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 pas vide , vous la traitez.

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

Il se peut que vous manquiez le fait de pouvoir appeler readline à la fin de session. 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.

5
seuvitor

C'est une autre modification du script de Tim Goldan qui fonctionne sur Linux et ajoute un simple observateur pour la modification de fichier en utilisant un dict (fichier => heure).

utilisation: quel que soit le nom.py chemin_accès_au_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 __== "__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

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

Je vous suggère de regarder IronPython , qui est une implémentation de .NET python . Avec IronPython, vous pouvez utiliser toutes les fonctionnalités de .NET, y compris

System.IO.FileSystemWatcher

Qui gère des fichiers uniques avec une interface simple Event.

3
gimel

Pour regarder un seul fichier avec polling, et des dépendances minimales, voici un exemple complet, basé sur la réponse de 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
3
4Oh4

Ceci est un exemple de vérification d'un fichier pour les modifications. 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.

Lorsque vous utilisez 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 chose Sinon, votre application restera 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

Dans le cas où vous vouliez 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 en faisant des électrons faire ce que vous voulez qu'ils fassent.

1
Bassim Huis

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 entendu, la classe englobante QMainWindow n’est pas strictement requise, c’est-à-dire. vous pouvez utiliser QFileSystemWatcher seul. 

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

On dirait que personne n'a 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 ça marche.

0
Gus

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

repyt ./app.py
0
Rafal Enden

La solution la meilleure 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)
0
george

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