Odczyt z często aktualizowanego pliku

Obecnie piszę program w Pythonie na systemie Linux. Celem jest odczytanie pliku dziennika i wykonanie polecenia bash po znalezieniu określonego ciągu znaków. Plik dziennika jest stale zapisywany przez inny program.

moje pytanie: jeśli otworzę plik przy użyciu metody open(), Czy mój obiekt pliku Pythona będzie aktualizowany w miarę, jak plik zostanie zapisany przez inny program, czy też będę musiał ponownie otwierać plik w odstępach czasowych?

UPDATE : Dzięki za odpowiedzi na razie. Być może powinienem wspomnieć, że plik jest zapisywany przez aplikację Java EE, więc nie mam kontroli nad tym, kiedy dane zostaną do niego zapisane. Obecnie mam program, który ponownie otwiera plik co 10 sekund i próbuje odczytać z pozycji bajtu w pliku, do którego ostatnio czytał. Na razie tylko drukuje sznurek, który został zwrócony. Miałem nadzieję, że plik nie musi być ponownie otwarty, ale polecenie read w jakiś sposób będzie miało dostęp do danych zapisanych do plik przez aplikację 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)
Dzięki za wskazówki dotyczące pyinotify I GENERATORÓW. Rzucę okiem na lepsze rozwiązanie.
Author: Jeff Bauer, 2011-03-24

7 answers

Polecam przyjrzeć się sztuczkom Davida Beazleya generatora dla Pythona , szczególnie Część 5: przetwarzanie nieskończonych danych. Będzie obsługiwać pythonowy odpowiednik tail -f logfile polecenia w czasie rzeczywistym.

# 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,
 102
Author: Jeff Bauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 19:51:12

"interaktywna sesja jest warta 1000 słów"

>>> 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'
Innymi słowy-tak, wystarczy jedno "otwarte".
 21
Author: jsbueno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-02 16:06:22

Oto nieco zmodyfikowana wersja Jeff Bauer odpowiedzi, która jest odporna na obcinanie plików. Bardzo przydatne, jeśli plik jest przetwarzany przez 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)
 8
Author: Andrew Druchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:40

Ponieważ celujesz w system Linux, możesz użyćpyinotify , aby powiadomić Cię o zmianie Pliku.

Jest też ta sztuczka, która może Ci się udać. Używa file.seek do tego, co robi tail -f.
 3
Author: nmichaels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-24 13:36:39

Nie jestem tutaj ekspertem, ale myślę, że będziesz musiał użyć jakiegoś wzorca obserwatora, aby pasywnie obserwować plik, a następnie odpalić zdarzenie, które ponownie otworzy plik, gdy nastąpi zmiana. Co do tego, jak to właściwie wdrożyć, nie mam pojęcia.

Nie sądzę, aby open() otworzyło plik w czasie rzeczywistym, jak sugerujesz.

 1
Author: Adam Pointer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-24 13:32:26

Jeśli masz kod odczytujący plik uruchomiony w pętli while:

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

I zapisujesz do tego samego pliku (w trybie dopisywania ) z innego programu. Jak tylko "jeden" zostanie dołączony do pliku, otrzymasz Wydruk. Możesz podjąć wszelkie działania, które chcesz podjąć. Krótko mówiąc, nie musisz ponownie otwierać pliku w regularnych odstępach czasu.

>>> f = open('/tmp/workfile', 'a')
>>> f.write("One\n")
>>> f.close()
>>> f = open('/tmp/workfile', 'a')
>>> f.write("ONE\n")
>>> f.close()
 1
Author: w00t,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-24 14:06:53

Mam podobny przypadek użycia i napisałem dla niego następujący fragment. Chociaż niektórzy mogą twierdzić, że nie jest to najbardziej idealny sposób, aby to zrobić, to robi zadanie i wygląda na wystarczająco łatwe do zrozumienia.

def reading_log_files(filename):
    with open(filename, "r") as f:
        data = f.read().splitlines()
    return data


def log_generator(filename, period=1):
    data = reading_log_files(filename)
    while True:
        time.sleep(period)
        new_data = reading_log_files(filename)
        yield new_data[len(data):]
        data = new_data


if __name__ == '__main__':
    x = log_generator(</path/to/log/file.log>)
    for lines in x:
        print(lines)
        # lines will be a list of new lines added at the end

Mam nadzieję, że okaże się to przydatne

 0
Author: noob_coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-10 17:34:24