2011-03-24 20 views
29

Actualmente estoy escribiendo un programa en python en un sistema Linux. El objetivo es leer un archivo de registro y ejecutar un comando bash al encontrar una cadena en particular. El archivo de registro está siendo escrito constantemente por otro programa. Mi pregunta es:Lectura de un archivo que se actualiza con frecuencia

Si abro el archivo usando el método open(), mi objeto de archivo Python se actualizará a medida que el otro programa escriba el archivo real o tendré que volver a abrir el archivo a intervalos de tiempo.

Gracias

Jim

ACTUALIZACIÓN: Gracias por las respuestas hasta ahora. Tal vez debería haber mencionado que el archivo está siendo escrito por una aplicación Java EE, por lo que no tengo control sobre cuándo se escriben los datos. Actualmente tengo un programa que vuelve a abrir el archivo cada 10 segundos e intenta leer desde la posición de bytes en el archivo que leyó por última vez. Por el momento solo imprime la cadena que se devuelve. Esperaba que el archivo no necesitara ser reabierto, pero el comando de lectura de alguna manera tendría acceso a los datos escritos en el archivo por la aplicación 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) 

Gracias por los consejos sobre pyinotify y generadores. Voy a echar un vistazo a estos para encontrar una mejor solución.

Respuesta

47

Recomendaría mirar David Beazley's Generator Tricks for Python, especialmente Parte 5: Procesamiento de datos infinitos. Controlará el equivalente de Python de un comando tail -f logfile en tiempo real.

# 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

Esta respuesta debe aceptarse – Quinma

+0

Superaría si la respuesta contuviera un ejemplo de código en términos del código del OP. –

+0

@ Chiel92: ejemplo de código agregado del sitio de David Beazley –

1

No soy un experto aquí pero creo que tendrá que usar algún tipo de patrón de observador para mirar pasivamente el archivo y luego disparar un evento que vuelva a abrir el archivo cuando se produzca un cambio. En cuanto a cómo realmente implementar esto, no tengo idea.

No creo que open() abra el archivo en tiempo real como usted sugiere.

3

Dado que su objetivo es un sistema Linux, puede usar pyinotify para notificarle cuando cambie el archivo.

También hay truco de this, que puede funcionar bien para usted. Utiliza file.seek para hacer lo que tail -f hace.

1

Si tiene el código de lectura del archivo se ejecuta en un bucle while:

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

y que está escribiendo a ese mismo archivo (en la modalidad de apertura) de otro programa. Tan pronto como se anexe "ONE" en el archivo, obtendrá la impresión. Puedes tomar cualquier acción que quieras tomar. En resumen, no tiene que volver a abrir el archivo a intervalos regulares.

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

Esta respuesta también es incorrecta, la escritura podría dividirse en 'ON' y 'E \ n' que daría como resultado dos líneas donde ninguno de los dos coincide. – Fabian

12

"Una sesión interactiva vale 1000 palabras"

>>> 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 otras palabras - sí, un solo "abrir" va a hacer.

+0

¡Esto es interesante de saber! –

2

Aquí hay una versión ligeramente modificada de la respuesta Jeff Bauer que es resistente al truncamiento de archivos. Muy útil si su archivo está siendo procesado por 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) 
Cuestiones relacionadas