2009-11-09 5 views
20

Wie sieht die pythonische Art aus, das Ende einer wachsenden Datei auf das Auftreten bestimmter Schlüsselwörter zu überwachen?Wie implementiert man ein Pythonic Äquivalent von Tail -F?

In Shell könnte ich sagen:

tail -f "$file" | grep "$string" | while read hit; do 
    #stuff 
done 
+0

Ähnliches: http://stackoverflow.com/questions/136168/tail-a-file-with-python – Mark

+2

Diese beiden Fragen scheinen identisch zu sein, aber in diesem Fall geht es darum, eine Datei ständig auf neue Zeilen zu überwachen, während die andere Frage ist über das Lesen der letzten x Zeilen – dbr

Antwort

17

Nun, der einfachste Weg, ständig aus der Datei gelesen werden würde, überprüfen, was neu ist und Test für die Treffer.

import time 

def watch(fn, words): 
    fp = open(fn, 'r') 
    while True: 
     new = fp.readline() 
     # Once all lines are read this just returns '' 
     # until the file changes and a new line appears 

     if new: 
      for word in words: 
       if word in new: 
        yield (word, new) 
     else: 
      time.sleep(0.5) 

fn = 'test.py' 
words = ['word'] 
for hit_word, hit_sentence in watch(fn, words): 
    print "Found %r in line: %r" % (hit_word, hit_sentence) 

Diese Lösung mit readline funktioniert, wenn Sie Ihre Daten einfach in den Zeilen angezeigt.

Wenn die Daten eine Art Stream sind, benötigen Sie einen Puffer, größer als die größte word, die Sie suchen, und füllen Sie es zuerst. Auf diese Weise wird es etwas komplizierter ...

+1

Ich bin ein wenig durch wiederholtes Öffnen der Datei skizziert. Sie können os.stat verwenden, um Ihnen mitzuteilen, ob die Datei geändert wurde. –

+9

Schau nochmal; open wird nur einmal aufgerufen. readline wird wiederholt aufgerufen. –

0

Nach meinem Wissen gibt es in der Python-Funktionsliste kein Äquivalent zu "tail". Die Lösung wäre, tell() (get file size) und read() zu verwenden, um die Endzeilen zu berechnen.

Dieser Blogbeitrag (nicht von mir) hat die Funktion ausgeschrieben, sieht mir passend aus! http://www.manugarg.com/2007/04/real-tailing-in-python.html

1

Wenn Sie das Problem nicht auf ein zeilenbasiertes Lesen beschränken können, müssen Sie auf Blöcke zurückgreifen.

sollte diese Arbeit:

import sys 

needle = "needle" 

blocks = [] 

inf = sys.stdin 

if len(sys.argv) == 2: 
    inf = open(sys.argv[1]) 

while True: 
    block = inf.read() 
    blocks.append(block) 
    if len(blocks) >= 2: 
     data = "".join((blocks[-2], blocks[-1])) 
    else: 
     data = blocks[-1] 

    # attention, this needs to be changed if you are interested 
    # in *all* matches separately, not if there was any match ata all 
    if needle in data: 
     print "found" 
     blocks = [] 
    blocks[:-2] = [] 

    if block == "": 
     break 

Die Herausforderung liegt darin, sicherzustellen, dass Sie Nadel übereinstimmen, auch wenn es durch zwei Blockgrenzen getrennt ist.

+0

Guter Fang; Ich schwimme den ganzen Tag in Logfiles, manchmal vergesse ich, dass nicht alle Daten in Zeilen kommen. – pra

2

EDIT: wie der Kommentar unten Notizen, funktioniert O_NONBLOCK nicht für Dateien auf der Festplatte. Dies wird immer noch helfen, wenn jemand kommt, um Schwanz Daten aus einem Socket oder Named Pipe oder einem anderen Prozess zu sehen, aber es beantwortet nicht die eigentliche Frage, die gestellt wurde. Die ursprüngliche Antwort bleibt für die Nachwelt erhalten. (Calling bis zum Schwanz und grep aus funktionieren wird, aber eine Nicht-Antwort von Art sowieso.)

Entweder öffnen Sie die Datei mit O_NONBLOCK und verwendet select für Lese Verfügbarkeit abfragen und dann read die neuen Daten zu lesen und den String Methoden zum Filtern von Zeilen am Ende einer Datei ... oder einfach das subprocess Modul verwenden und tail und grep die Arbeit für Sie erledigen lassen, so wie Sie es in der Shell tun würden.

+1

Nicht blockierende E/A wird für Dateien auf den meisten (wenn nicht allen) Betriebssystemen nicht unterstützt. Dazu gehören Linux und FreeBSD. Wenn dies der Fall wäre, wäre die Kombination von nicht blockierendem IO und poll/select/was auch immer dasselbe wie blockierendes Lesen, was nicht das tut, was das OP benötigt. – WGH

+0

@WGH: Guter Punkt! Ich denke, dass ich das nach dem Schreiben gelernt habe, aber es ist wichtig für jeden, der später zu dieser Frage kommt, zu notieren, also habe ich meine Antwort mit einem entsprechenden Disclaimer versehen. –

0

Sie können collections.deque verwenden, um Schwanz zu implementieren.

Von http://docs.python.org/library/collections.html#deque-recipes ...

def tail(filename, n=10): 
    'Return the last n lines of a file' 
    return deque(open(filename), n) 

Natürlich liest diese den gesamten Dateiinhalt, aber es ist eine saubere und prägnante Art und Weise Schwanz umzusetzen.

+3

Das beantwortet eine andere Frage. – WGH

5
def tail(f): 
    f.seek(0, 2) 

    while True: 
     line = f.readline() 

     if not line: 
      time.sleep(0.1) 
      continue 

     yield line 

def process_matches(matchtext): 
    while True: 
     line = (yield) 
     if matchtext in line: 
      do_something_useful() # email alert, etc. 


list_of_matches = ['ERROR', 'CRITICAL'] 
matches = [process_matches(string_match) for string_match in list_of_matches]  

for m in matches: # prime matches 
    m.next() 

while True: 
    auditlog = tail(open(log_file_to_monitor)) 
    for line in auditlog: 
     for m in matches: 
      m.send(line) 

Ich verwende dies, um Protokolldateien zu überwachen. In der vollständigen Implementierung behalte ich list_of_matches in einer Konfigurationsdatei, so dass sie für mehrere Zwecke verwendet werden kann. Auf meiner Liste der Verbesserungen ist die Unterstützung für Regex anstelle eines einfachen "In" -Match.

+0

Das ist eine seltsame/interessante Verwendung von Generatoren. – dbr

3

Sie können auswählen, um nach neuen Inhalten in einer Datei zu suchen.

def tail(filename, bufsize = 1024): 
    fds = [ os.open(filename, os.O_RDONLY) ] 
    while True: 
     reads, _, _ = select.select(fds, [], []) 
     if 0 < len(reads): 
      yield os.read(reads[0], bufsize) 
+4

Sie können Select nicht verwenden, um Dateien für neue Daten - nur Sockets abzurufen. Siehe: http://docs.python.org/library/select.html, erster Absatz letzter Satz: Es kann nicht in regulären Dateien verwendet werden, um festzustellen, ob eine Datei seit dem letzten Lesen gewachsen ist. – synthesizerpatel

5

Sie pytailf verwenden: -f Wrapper Einfache Python Schwanz

from tailf import tailf  

for line in tailf("myfile.log"): 
    print line 
+0

Dies funktioniert nicht für Python 3. – James

+0

Verschwenden Sie nicht Ihre Zeit mit tailf, es ruft intern "/ usr/bin/tail", was sicherlich nicht das ist, was Sie wollen. –

1

Wenn Sie einfach Python 3 brauchen nur eine tote Lösung für die Verarbeitung die Zeilen einer Textdatei, wie sie geschrieben sind, und Sie brauchen keine Windows-Unterstützung, das hat gut für mich funktioniert:

import subprocess 
def tailf(filename): 
    #returns lines from a file, starting from the beginning 
    command = "tail -n +1 -F " + filename 
    p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, universal_newlines=True) 
    for line in p.stdout: 
     yield line 
for line in tailf("logfile"): 
    #do stuff 

Es blockiert das Warten auf das Schreiben neuer Zeilen, daher ist es nicht für asynchrone Verwendung ohne einige Änderungen geeignet.

Verwandte Themen