Ich möchte die Ausgabe von tail -F oder ähnlichem in Python für mich verfügbar machen, ohne sie zu blockieren oder zu sperren. Ich habe einige wirklich alten Code zu tun , dass gefunden hier , aber ich denke , es muss einen besseren Weg oder eine Bibliothek sein , die gleiche Sache jetzt zu tun. Kennt jemand einen?
Im Idealfall hätte ich so etwas tail.getNewData()
, das ich jedes Mal anrufen könnte, wenn ich mehr Daten wollte.
subprocess.call(["tail", "-F", filename])
get_new_data
Methode (PEP-8-Name) alle Daten seit dem letzten Aufruf oder nur den aktuellen Schwanz zurückgeben (möglicherweise gehen einige Daten verloren)?Antworten:
Nicht blockierend
Wenn Sie unter Linux arbeiten (da Windows das Aufrufen von select on files nicht unterstützt), können Sie das Unterprozessmodul zusammen mit dem select-Modul verwenden.
import time import subprocess import select f = subprocess.Popen(['tail','-F',filename],\ stdout=subprocess.PIPE,stderr=subprocess.PIPE) p = select.poll() p.register(f.stdout) while True: if p.poll(1): print f.stdout.readline() time.sleep(1)
Dadurch wird die Ausgabepipe nach neuen Daten abgefragt und gedruckt, sobald sie verfügbar sind. Normalerweise wird das
time.sleep(1)
undprint f.stdout.readline()
durch nützlichen Code ersetzt.Blockierung
Sie können das Unterprozessmodul ohne die zusätzlichen Aufrufe des Auswahlmoduls verwenden.
import subprocess f = subprocess.Popen(['tail','-F',filename],\ stdout=subprocess.PIPE,stderr=subprocess.PIPE) while True: line = f.stdout.readline() print line
Dadurch werden auch neue Zeilen gedruckt, wenn sie hinzugefügt werden, aber es wird blockiert, bis das Endprogramm geschlossen wird, wahrscheinlich mit
f.kill()
.quelle
f.stdout
handelt es sich um eine Pipe, nicht um eine Datei (aber ich glaube, Windows kann sie immer noch nicht verwendenselect
).print line
Verwenden Siesys.stdout.write(line)
in der Lösung "Blockieren" stattdessen " Zusätzliche Zeilenumbrüche", die beim Einfügen eingefügt werden..strip()
würde auch führende Leerzeichen entfernen, die von Bedeutung sein könnten.Verwenden des sh-Moduls (pip install sh):
from sh import tail # runs forever for line in tail("-f", "/var/log/some_log_file.log", _iter=True): print(line)
[aktualisieren]
Da sh.tail with
_iter
= True ein Generator ist, können Sie:import sh tail = sh.tail("-f", "/var/log/some_log_file.log", _iter=True)
Dann können Sie "getNewData" mit:
Beachten Sie, dass der Endpuffer, wenn er leer ist, blockiert wird, bis weitere Daten vorhanden sind (aus Ihrer Frage geht nicht hervor, was Sie in diesem Fall tun möchten).
[aktualisieren]
Ein Containergenerator, der den Tail-Aufruf innerhalb einer while-True-Schleife platziert und eventuelle E / A-Ausnahmen abfängt, hat fast den gleichen Effekt wie -F.
def tail_F(some_file): while True: try: for line in sh.tail("-f", some_file, _iter=True): yield line except sh.ErrorReturnCode_1: yield None
Wenn auf die Datei nicht mehr zugegriffen werden kann, gibt der Generator None zurück. Es wird jedoch weiterhin blockiert, bis neue Daten vorliegen, wenn auf die Datei zugegriffen werden kann. Es bleibt mir unklar, was Sie in diesem Fall tun möchten.
Der Ansatz von Raymond Hettinger scheint ziemlich gut zu sein:
def tail_F(some_file): first_call = True while True: try: with open(some_file) as input: if first_call: input.seek(0, 2) first_call = False latest_data = input.read() while True: if '\n' not in latest_data: latest_data += input.read() if '\n' not in latest_data: yield '' if not os.path.isfile(some_file): break continue latest_lines = latest_data.split('\n') if latest_data[-1] != '\n': latest_data = latest_lines[-1] else: latest_data = input.read() for line in latest_lines[:-1]: yield line + '\n' except IOError: yield ''
Dieser Generator gibt '' zurück, wenn auf die Datei nicht mehr zugegriffen werden kann oder wenn keine neuen Daten vorhanden sind.
[aktualisieren]
Ich denke, die zweite gibt die letzten zehn Zeilen aus, wenn der Endprozess endet, und
-f
zwar immer dann, wenn ein E / A-Fehler vorliegt. Dastail --follow --retry
Verhalten ist in den meisten Fällen, die ich mir in Unix-ähnlichen Umgebungen vorstellen kann, nicht weit davon entfernt.Wenn Sie Ihre Frage aktualisieren, um zu erklären, was Ihr eigentliches Ziel ist (der Grund, warum Sie die Schwanzwiederholung imitieren möchten), erhalten Sie möglicherweise eine bessere Antwort.
Natürlich zeigt tail standardmäßig die letzten 10 Zeilen an ... Sie können den Dateizeiger mit file.seek am Ende der Datei positionieren. Ich überlasse dem Leser eine ordnungsgemäße Implementierung als Übung.
Meiner Meinung nach ist der file.read () -Ansatz weitaus eleganter als eine auf Subprozessen basierende Lösung.
quelle
tail
Aufruf in einewhile True
Schleife platziert und eventuelle E / A-Ausnahmen abfängt, hat den gleichen Effekt wie -F.file.read()
Ansatz eleganter zu sein ?tail
Behandelt ordnungsgemäß das Anzeigen der letzten 10 Zeilen der Datei (auch wenn die Zeilen sehr groß sind), das Lesen neuer Zeilen für immer, das Aufwachen beim Eintreffen neuer Zeilen (plattformabhängig) und das Öffnen neuer Dateien bei Bedarf. Mit einem Wort, das Dienstprogramm ist ziemlich gut für das konzipiert, was es tun soll - die Neuimplementierung scheint bei weitem nicht so elegant zu sein. (Ich gebe jedoch zu, dass dassh
Modul ziemlich geschickt ist.)Der einzige tragbare Weg zu
tail -f
einer Datei scheint darin zu bestehen, daraus zu lesen und (nach asleep
) erneut zu versuchen, wennread
0 zurückgegeben wird. Dietail
Dienstprogramme auf verschiedenen Plattformen verwenden plattformspezifische Tricks (z. B.kqueue
auf BSD), um eine Datei für immer effizient zu verwalten ohne zu brauchensleep
.Daher ist die Implementierung eines Goods
tail -f
nur in Python wahrscheinlich keine gute Idee, da Sie die Implementierung mit dem kleinsten gemeinsamen Nenner verwenden müssten (ohne auf plattformspezifische Hacks zurückzugreifen). Mit einem einfachensubprocess
Öffnentail -f
und Durchlaufen der Zeilen in einem separaten Thread können Sie problemlos eine nicht blockierendetail
Operation in Python implementieren .Beispielimplementierung:
import threading, Queue, subprocess tailq = Queue.Queue(maxsize=10) # buffer at most 100 lines def tail_forever(fn): p = subprocess.Popen(["tail", "-f", fn], stdout=subprocess.PIPE) while 1: line = p.stdout.readline() tailq.put(line) if not line: break threading.Thread(target=tail_forever, args=(fn,)).start() print tailq.get() # blocks print tailq.get_nowait() # throws Queue.Empty if there are no lines to read
quelle
tail -F
in sie zu leiten . Ich kann nicht verstehen, warum das Hinzufügen der Komplexität von Threading, Warteschlange und Unterprozess zu einem Vorteil gegenüber dem herkömmlichen Ansatz führt.Das kommt also ziemlich spät, aber ich bin wieder auf dasselbe Problem gestoßen, und es gibt jetzt eine viel bessere Lösung. Verwenden Sie einfach Pygtail :
quelle
Wenn Sie die Antwort von Ijaz Ahmad Khan so anpassen, dass Zeilen nur dann ausgegeben werden, wenn sie vollständig geschrieben sind (Zeilen enden mit einem Zeilenumbruchzeichen), erhalten Sie eine pythonische Lösung ohne externe Abhängigkeiten:
def follow(file) -> Iterator[str]: """ Yield each line from a file as they are written. """ line = '' while True: tmp = file.readline() if tmp is not None: line += tmp if line.endswith("\n"): yield line line = '' else: time.sleep(0.1) if __name__ == '__main__': for line in follow(open("test.txt", 'r')): print(line, end='')
quelle
Wir haben bereits eine und es ist sehr schön. Rufen Sie einfach f.read () auf, wenn Sie weitere Daten wünschen. Es beginnt dort zu lesen, wo der vorherige Lesevorgang aufgehört hat, und es liest das Ende des Datenstroms durch:
f = open('somefile.log') p = 0 while True: f.seek(p) latest_data = f.read() p = f.tell() if latest_data: print latest_data print str(p).center(10).center(80, '=')
Verwenden Sie zum zeilenweisen Lesen f.readline () . Manchmal endet die gelesene Datei mit einer teilweise gelesenen Zeile. Behandeln Sie diesen Fall, indem f.tell () die aktuelle Dateiposition ermittelt und mit f.seek () den Dateizeiger wieder an den Anfang der unvollständigen Zeile bewegt. In diesem ActiveState-Rezept finden Sie Informationen zum Arbeitscode.
quelle
Alle Antworten, die tail -f verwenden, sind nicht pythonisch.
Hier ist der pythonische Weg: (ohne externes Tool oder Bibliothek)
def follow(thefile): while True: line = thefile.readline() if not line or not line.endswith('\n'): 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, end='')
quelle
Sie können die 'Tailer'-Bibliothek verwenden: https://pypi.python.org/pypi/tailer/
Es besteht die Möglichkeit, die letzten Zeilen abzurufen:
# Get the last 3 lines of the file tailer.tail(open('test.txt'), 3) # ['Line 9', 'Line 10', 'Line 11']
Und es kann auch einer Datei folgen:
# Follow the file as it grows for line in tailer.follow(open('test.txt')): print line
Wenn man schwanzartiges Verhalten will, scheint dies eine gute Option zu sein.
quelle
follow()
die gleiche Datei, nachdem es entfernt / neu erstellt wurde, also hat es bei mir nicht funktioniert: /make-all-the-things-work-by-themselves
stattdessen das Pypi-ModulEine weitere Option ist die
tailhead
Bibliothek, die sowohl Python-Versionen vontail
als auchhead
Dienstprogramme und API bereitstellt, die in Ihrem eigenen Modul verwendet werden können.Ursprünglich basierend auf dem
tailer
Modul, besteht sein Hauptvorteil in der Möglichkeit, Dateien nach Pfad zu folgen, dh es kann Situationen behandeln, in denen Dateien neu erstellt werden. Außerdem hat es einige Fehlerbehebungen für verschiedene Randfälle.quelle
Python ist "Batterien enthalten" - es hat eine gute Lösung dafür: https://pypi.python.org/pypi/pygtail
Liest nicht gelesene Protokolldateizeilen. Erinnert sich, wo es das letzte Mal beendet wurde, und fährt von dort fort.
import sys from pygtail import Pygtail for line in Pygtail("some.log"): sys.stdout.write(line)
quelle
Sie können auch den Befehl 'AWK' verwenden.
Weitere Informationen finden Sie unter : http://www.unix.com/shell-programming-scripting/41734-how-print-specific-lines-awk.html
awk kann verwendet werden, um die letzte Zeile, die letzten Zeilen oder eine beliebige Zeile in a zu beenden Datei.
Dies kann von Python aus aufgerufen werden.
quelle
Wenn Sie unter Linux arbeiten, implementieren Sie eine nicht blockierende Implementierung in Python auf folgende Weise.
import subprocess subprocess.call('xterm -title log -hold -e \"tail -f filename\"&', shell=True, executable='/bin/csh') print "Done"
quelle