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.
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.
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 ...
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...
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)
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)
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.
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!
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' \
.
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
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 .
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.
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
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.
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
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.
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.
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
...
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.
Vous pouvez également utiliser une simple bibliothèque appelée repyt , voici un exemple:
repyt ./app.py
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)
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