2011-03-24 5 views
29

J'écris actuellement un programme en python sur un système Linux. L'objectif est de lire un fichier journal et d'exécuter une commande bash lors de la recherche d'une chaîne particulière. Le fichier journal est constamment écrit par un autre programme. Ma question est:Lecture à partir d'un fichier fréquemment mis à jour

Si j'ouvre le fichier en utilisant la méthode open(), mon objet fichier Python sera-t-il mis à jour alors que le fichier sera écrit par l'autre programme ou devrai-je rouvrir le fichier à intervalles réguliers?

Merci

Jim

MISE À JOUR: Merci pour les réponses à ce jour. J'aurais peut-être dû mentionner que le fichier est en train d'être écrit par une application Java EE, donc je n'ai aucun contrôle sur le moment où les données sont écrites dessus. J'ai actuellement un programme qui rouvre le fichier toutes les 10 secondes et essaie de lire à partir de la position d'octet dans le fichier qu'il a lu pour la dernière fois. Pour l'instant, il ne fait qu'imprimer la chaîne retournée. J'espérais que le fichier n'avait pas besoin d'être rouvert mais la commande de lecture aurait en quelque sorte accès aux données écrites dans le fichier par l'application Java.

#!/usr/bin/python 
import time 

fileBytePos = 0 
while True: 
    inFile = open('./server.log','r') 
    inFile.seek(fileBytePos) 
    data = inFile.read() 
    print data 
    fileBytePos = inFile.tell() 
    print fileBytePos 
    inFile.close() 
    time.sleep(10) 

Merci pour les conseils sur pyinotify et les générateurs. Je vais jeter un oeil à ceux-ci pour une meilleure solution.

Répondre

47

Je vous recommande de regarder Generator Tricks for Python de David Beazley, en particulier Partie 5: Traitement des données infinies. Il va gérer l'équivalent Python d'une commande tail -f logfile en temps réel.

# follow.py 
# 
# Follow a file like tail -f. 

import time 
def follow(thefile): 
    thefile.seek(0,2) 
    while True: 
     line = thefile.readline() 
     if not line: 
      time.sleep(0.1) 
      continue 
     yield line 

if __name__ == '__main__': 
    logfile = open("run/foo/access-log","r") 
    loglines = follow(logfile) 
    for line in loglines: 
     print line, 
+2

Cette réponse doit être acceptée – Quinma

+0

J'upvote si la réponse contient un exemple de code en termes de code de l'OP. –

+0

@ Chiel92: échantillon de code ajouté à partir du site de David Beazley –

1

Je ne suis pas un expert ici, mais je pense que vous devrez utiliser une sorte de modèle d'observateur pour regarder passivement le fichier puis déclencher un événement qui rouvre le fichier quand un changement se produit. Quant à savoir comment mettre en œuvre réellement cela, je n'en ai aucune idée.

Je ne pense pas que open() ouvrira le fichier en temps réel comme vous le suggérez.

3

Puisque vous ciblez un système Linux, vous pouvez utiliser pyinotify pour vous avertir lorsque le fichier change.

Il y a aussi this astuce, ce qui peut bien fonctionner pour vous. Il utilise file.seek pour faire ce que fait tail -f.

1

Si vous avez le code lecture du fichier en cours d'exécution dans une boucle while:

f = open('/tmp/workfile', 'r') 
while(1): 
    line = f.readline() 
    if line.find("ONE") != -1: 
     print "Got it" 

et vous écrivez à ce même fichier (en mode append) d'un autre programme. Dès que "ONE" est ajouté dans le fichier, vous obtiendrez l'impression. Vous pouvez prendre n'importe quelle action que vous voulez prendre. En bref, vous n'avez pas à rouvrir le fichier à intervalles réguliers.

>>> f = open('/tmp/workfile', 'a') 
>>> f.write("One\n") 
>>> f.close() 
>>> f = open('/tmp/workfile', 'a') 
>>> f.write("ONE\n") 
>>> f.close() 
+0

Cette réponse est également fausse, l'écriture pourrait se diviser en "ON" et "E \ n" qui se traduirait par deux lignes où aucun ne correspond. – Fabian

12

"Une session interactive vaut 1000 mots"

>>> f1 = open("bla.txt", "wt") 
>>> f2 = open("bla.txt", "rt") 
>>> f1.write("bleh") 
>>> f2.read() 
'' 
>>> f1.flush() 
>>> f2.read() 
'bleh' 
>>> f1.write("blargh") 
>>> f1.flush() 
>>> f2.read() 
'blargh' 

En d'autres termes - oui, un seul "ouvert" fera.

+0

C'est intéressant à savoir! –

2

Voici une version légèrement modifiée de Jeff Bauer qui résiste à la troncature de fichiers. Très utile si votre fichier est en cours de traitement par logrotate.

import os 
import time 

def follow(name): 
    current = open(name, "r") 
    curino = os.fstat(current.fileno()).st_ino 
    while True: 
     while True: 
      line = current.readline() 
      if not line: 
       break 
      yield line 

     try: 
      if os.stat(name).st_ino != curino: 
       new = open(name, "r") 
       current.close() 
       current = new 
       curino = os.fstat(current.fileno()).st_ino 
       continue 
     except IOError: 
      pass 
     time.sleep(1) 


if __name__ == '__main__': 
    fname = "test.log" 
    for l in follow(fname): 
     print "LINE: {}".format(l) 
Questions connexes