So vervollständigen Sie den Tail-Befehl mit Python
1. Die erste Version – Echtzeitdaten vom Ende der Datei lesen
Die Hauptidee ist: Öffnen Sie die Datei, bewegen Sie den Zeiger an das Ende der Datei, geben Sie dann die Daten aus, wenn Daten vorhanden sind, und schlafen Sie für einen bestimmten Zeitraum, wenn keine Daten vorhanden sind -f Standardmäßig werden zuerst die letzten 10 Datenzeilen gelesen und dann Echtzeitdaten vom Ende der Datei gelesen. Bei kleinen Dateien können Sie zuerst den gesamten Dateiinhalt lesen und die letzten 10 ausgeben Die Leistung, den vollständigen Text zu lesen und dann die letzten 10 Zeilen abzurufen, ist jedoch nicht hoch, und die Randbedingung für das Zurücksetzen von 10 Zeilen ist auch sehr kompliziert Holen Sie sich die letzten 10 Zeilen:
rrree Sie können sehen, dass die Implementierung im Vergleich zur ersten Version nur noch eine read_last_line-Funktion
ist. Als nächstes müssen wir das Leistungsproblem lösen Da die Datei sehr groß ist, funktioniert diese Logik nicht. Wenn alle Protokolldateien ausgelesen werden, kann im Linux-System kein Zeiger angegeben werden Um zu den letzten 10 Zeilen zu springen, können Sie nur die folgende Methode verwenden, um die Ausgabe der letzten 10 Zeilen zu simulieren:
Zuerst springt der Cursor zur neuesten Zeichen und speichert den aktuellen Cursor und schätze dann die Zeichenlänge einer Datenzeile. Es ist am besten, sie zu lang zu sein. Hier verarbeite ich sie basierend auf der Länge von 1024 Zeichen für eine Zeile
- Verwenden Sie dann die Suchmethode, um zu suchen (-1024 * 10, 2) Zeichen, dies ist unser geschätzter Inhalt innerhalb der letzten 10 Zeilen
Dann beurteilen wir den Inhalt anhand der Länge des Sprungs Wenn die Zeichenlänge kleiner als 10 * 1024 ist, wird bewiesen, dass die gesamte Datei nicht 10 Zeilen enthält Der Sprung entspricht 1024 * 10. Berechnen Sie dann mithilfe des Zeilenumbruchzeichens, wie viele Zeilen die Zeichenlänge beträgt. Wenn die Zahl größer als 10 ist, werden nur die letzten 10 Zeilen ausgegeben gelesen wurden, lesen Sie 6*1024 weiter, bis 10 Zeilen gelesen wurden
Nach den oben genannten Schritten zählen Sie bis 10 herunter. Die Zeilendaten wurden berechnet und können ausgedruckt werden, und Sie können fortfahren Um Daten anzuhängen, hat sich zu diesem Zeitpunkt möglicherweise auch unser Cursor geändert. Zu diesem Zeitpunkt muss der Cursor zum gerade gespeicherten Cursor zurückgesprungen sein. Oder die Daten werden wiederholt gedruckt tail -f
默认先读取最后10行数据,再从文件尾部读取实时数据.如果对于小文件,可以先读取所有文件内容,并输出最后10行, 但是读取全文再获取最后10行的性能不高, 而从后滚10行的边界条件也很复杂, 先看先读取全文再获取最后10行的实现:
import time import sys from typing import Callable, NoReturn class Tail(object): def __init__( self, file_name: str, output: Callable[[str], NoReturn] = sys.stdout.write, interval: int = 1 ): self.file_name: str = file_name self.output: Callable[[str], NoReturn] = output self.interval: int = interval def __call__(self): with open(self.file_name) as f: f.seek(0, 2) # 从文件结尾处开始seek while True: line: str = f.readline() if line: self.output(line) # 使用print都会每次都打印新的一行 else: time.sleep(self.interval) if __name__ == '__main__': filename: str = sys.argv[0] Tail(filename)()
可以看到实现很简单, 相比第一版只多了个read_last_line的函数
, 接下来就要解决性能的问题了, 当文件很大的时候, 这个逻辑是不行的, 特别是有些日志文件经常有几个G大, 如果全读出来内存就爆了. 而在Linux系统中, 没有一个接口可以指定指针跳到倒数10行, 只能使用如下方法来模拟输出倒数10行:
首先游标跳转到最新的字符, 保存当前游标, 然后预估一行数据的字符长度, 最好偏多, 这里我按1024字符长度为一行来处理
然后利用seek的方法,跳转到seek(-1024 * 10, 2)的字符, 这就是我们预估的倒数10行内的内容
接着对内容进行判断, 如果跳转的字符长度小于 10 * 1024, 则证明整个文件没有10行, 则采用原来的
read_last_line
方法.如果跳转到字符长度等于1024 * 10, 则利用换行符计算已取字符长度共有多少行,如果行数大于10,那只输出最后10行,如果只读了4行,则继续读6*1024,直到读满10行为止
通过以上步奏, 就把倒数10行的数据计算好了可以打印出来, 可以进入追加数据了, 但是这时候文件内容可能发生改变了, 我们的游标也发生改变了, 这时候要把游标跳回到刚才保存的游标,防止漏打或者重复打印数据.
分析完毕后, 就可以开始重构read_last_line
函数了.
import time import sys from typing import Callable, NoReturn class Tail(object): def __init__( self, file_name: str, output: Callable[[str], NoReturn] = sys.stdout.write, interval: int = 1 ): self.file_name: str = file_name self.output: Callable[[str], NoReturn] = output self.interval: int = interval def __call__(self): with open(self.file_name) as f: self.read_last_line(f) while True: line: str = f.readline() if line: self.output(line) # 使用print都会每次都打印新的一行 else: time.sleep(self.interval) def read_last_line(self, f): last_lines = f.readlines()[-10:] for line in last_lines: self.output(line) if __name__ == '__main__': filename: str = sys.argv[0] Tail(filename)()
3.第三版--优雅的读取输出日志文件
可以发现实时读取那块的逻辑性能还是很差, 如果每秒读一次文件,实时性就太慢了,把间隔改小了,则处理器占用太多. 性能最好的情况是如果能得知文件更新再进行打印文件, 那性能就能得到保障了.庆幸的是,在Linux中inotify
提供了这样的功能. 此外,日志文件有一个特点就是会进行logrotate,如果日志被logrotate了,那我们就需要重新打开文件,并进一步读取数据, 这种情况也可以利用到inotify
, 当inotify
Nachdem die Analyse abgeschlossen ist, können Sie mit der Rekonstruktion der Funktion read_last_line
beginnen.
import time import sys from typing import Callable, List, NoReturn class Tail(object): def __init__( self, file_name: str, output: Callable[[str], NoReturn] = sys.stdout.write, interval: int = 1, len_line: int = 1024 ): self.file_name: str = file_name self.output: Callable[[str], NoReturn] = output self.interval: int = interval self.len_line: int = len_line def __call__(self, n: int = 10): with open(self.file_name) as f: self.read_last_line(f, n) while True: line: str = f.readline() if line: self.output(line) # 使用print都会每次都打印新的一行 else: time.sleep(self.interval) def read_last_line(self, file, n): read_len: int = self.len_line * n # 跳转游标到最后 file.seek(0, 2) # 获取当前结尾的游标位置 now_tell: int = file.tell() while True: if read_len > file.tell(): # 如果跳转的字符长度大于原来文件长度,那就把所有文件内容打印出来 file.seek(0) # 由于read方法是按照游标进行打印, 所以要重置游标 last_line_list: List[str] = file.read().split('\n')[-n:] # 重新获取游标位置 now_tell: int = file.tell() break # 跳转到我们预估的字符位置 file.seek(-read_len, 2) read_str: str = file.read(read_len) cnt: int = read_str.count('\n') if cnt >= n: # 如果获取的行数大于要求的行数,则获取前n行的行数 last_line_list: List[str] = read_str.split('\n')[-n:] break else: # 如果获取的行数小于要求的行数,则预估需要获取的行数,继续获取 if cnt == 0: line_per: int = read_len else: line_per: int = int(read_len / cnt) read_len = line_per * n for line in last_line_list: self.output(line + '\n') # 重置游标,确保接下来打印的数据不重复 file.seek(now_tell) if __name__ == '__main__': import argparse parser = argparse.ArgumentParser() parser.add_argument("-f", "--filename") parser.add_argument("-n", "--num", default=10) args, unknown = parser.parse_known_args() if not args.filename: raise RuntimeError('filename args error') Tail(args.filename)(int(args.num))
3. Die Ausgabeprotokolldatei kann elegant gelesen werden Die Leistung beim Abrufen dieses Blocks ist immer noch sehr schlecht. Wenn die Datei einmal pro Sekunde gelesen wird, ist die Echtzeitleistung zu langsam. Wenn das Intervall auf einen kleineren Wert geändert wird, belegt der Prozessor zu viel Wenn die Dateiaktualisierung bekannt ist und dann die Leistung garantiert werden kann, bietet inotify
unter Linux eine solche Funktion Wenn das Protokoll logrotiert wird, müssen wir die Datei erneut öffnen und die Daten weiter lesen. In diesem Fall kann auch inotify
verwendet werden Für den Fall, dass die Datei erneut geöffnet wird, öffnen wir die Datei einfach erneut und lesen sie erneut.
import os import sys from typing import Callable, List, NoReturn import pyinotify multi_event = pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF # 监控多个事件 class InotifyEventHandler(pyinotify.ProcessEvent): # 定制化事件处理类,注意继承 """ 执行inotify event的封装 """ f: 'open()' filename: str path: str wm: 'pyinotify.WatchManager' output: Callable def my_init(self, **kargs): """pyinotify.ProcessEvent要求不能直接继承__init__, 而是要重写my_init, 我们重写这一段并进行初始化""" # 获取文件 filename: str = kargs.pop('filename') if not os.path.exists(filename): raise RuntimeError('Not Found filename') if '/' not in filename: filename = os.getcwd() + '/' + filename index = filename.rfind('/') if index == len(filename) - 1 or index == -1: raise RuntimeError('Not a legal path') self.f = None self.filename = filename self.output: Callable = kargs.pop('output') self.wm = kargs.pop('wm') # 只监控路径,这样就能知道文件是否移动 self.path = filename[:index] self.wm.add_watch(self.path, multi_event) def read_line(self): """统一的输出方法""" for line in self.f.readlines(): self.output(line) def process_IN_MODIFY(self, event): """必须为process_事件名称,event表示事件对象, 这里表示监控到文件发生变化, 进行文件读取""" if event.pathname == self.filename: self.read_line() def process_IN_MOVE_SELF(self, event): """必须为process_事件名称,event表示事件对象, 这里表示监控到文件发生重新打开, 进行文件读取""" if event.pathname == self.filename: # 检测到文件被移动重新打开文件 self.f.close() self.f = open(self.filename) self.read_line() def __enter__(self) -> 'InotifyEventHandler': self.f = open(self.filename) return self def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() class Tail(object): def __init__( self, file_name: str, output: Callable[[str], NoReturn] = sys.stdout.write, interval: int = 1, len_line: int = 1024 ): self.file_name: str = file_name self.output: Callable[[str], NoReturn] = output self.interval: int = interval self.len_line: int = len_line wm = pyinotify.WatchManager() # 创建WatchManager对象 inotify_event_handler = InotifyEventHandler( **dict(filename=file_name, wm=wm, output=output) ) # 实例化我们定制化后的事件处理类, 采用**dict传参数 wm.add_watch('/tmp', multi_event) # 添加监控的目录,及事件 self.notifier = pyinotify.Notifier(wm, inotify_event_handler) # 在notifier实例化时传入,notifier会自动执行 self.inotify_event_handle: 'InotifyEventHandler' = inotify_event_handler def __call__(self, n: int = 10): """通过inotify的with管理打开文件""" with self.inotify_event_handle as i: # 先读取指定的行数 self.read_last_line(i.f, n) # 启用inotify的监听 self.notifier.loop() def read_last_line(self, file, n): read_len: int = self.len_line * n # 获取当前结尾的游标位置 file.seek(0, 2) now_tell: int = file.tell() while True: if read_len > file.tell(): # 如果跳转的字符长度大于原来文件长度,那就把所有文件内容打印出来 file.seek(0) last_line_list: List[str] = file.read().split('\n')[-n:] # 重新获取游标位置 now_tell: int = file.tell() break file.seek(-read_len, 2) read_str: str = file.read(read_len) cnt: int = read_str.count('\n') if cnt >= n: # 如果获取的行数大于要求的行数,则获取前n行的行数 last_line_list: List[str] = read_str.split('\n')[-n:] break else: # 如果获取的行数小于要求的行数,则预估需要获取的行数,继续获取 if cnt == 0: line_per: int = read_len else: line_per: int = int(read_len / cnt) read_len = line_per * n for line in last_line_list: self.output(line + '\n') # 重置游标,确保接下来打印的数据不重复 file.seek(now_tell) if __name__ == '__main__': import argparse parser = argparse.ArgumentParser() parser.add_argument("-f", "--filename") parser.add_argument("-n", "--num", default=10) args, unknown = parser.parse_known_args() if not args.filename: raise RuntimeError('filename args error') Tail(args.filename)(int(args.num))
Das obige ist der detaillierte Inhalt vonSo vervollständigen Sie den Tail-Befehl mit Python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Die XML -Verschönerung verbessert im Wesentlichen seine Lesbarkeit, einschließlich angemessener Einkerbung, Zeilenpausen und Tag -Organisation. Das Prinzip besteht darin, den XML -Baum zu durchqueren, die Eindrücke entsprechend der Ebene hinzuzufügen und leere Tags und Tags, die Text enthalten, zu verarbeiten. Pythons xml.etree.elementtree -Bibliothek bietet eine bequeme Funktion hübsch_xml (), die den oben genannten Verschönerungsprozess implementieren kann.

Verwenden Sie die meisten Texteditoren, um XML -Dateien zu öffnen. Wenn Sie eine intuitivere Baumanzeige benötigen, können Sie einen XML -Editor verwenden, z. B. Sauerstoff XML -Editor oder XMLSPY. Wenn Sie XML -Daten in einem Programm verarbeiten, müssen Sie eine Programmiersprache (wie Python) und XML -Bibliotheken (z. B. XML.etree.elementtree) verwenden, um zu analysieren.

Das Ändern des XML -Inhalts erfordert die Programmierung, da die Zielknoten genau aufgefasst werden müssen, um hinzuzufügen, zu löschen, zu ändern und zu überprüfen. Die Programmiersprache verfügt über entsprechende Bibliotheken, um XML zu verarbeiten, und bietet APIs zur Durchführung sicherer, effizienter und steuerbarer Vorgänge wie Betriebsdatenbanken.

Eine Anwendung, die XML direkt in PDF konvertiert, kann nicht gefunden werden, da es sich um zwei grundlegend unterschiedliche Formate handelt. XML wird zum Speichern von Daten verwendet, während PDF zur Anzeige von Dokumenten verwendet wird. Um die Transformation abzuschließen, können Sie Programmiersprachen und Bibliotheken wie Python und ReportLab verwenden, um XML -Daten zu analysieren und PDF -Dokumente zu generieren.

Es gibt kein einfaches und direktes kostenloses XML -zu -PDF -Tool auf Mobilgeräten. Der erforderliche Datenvisualisierungsprozess beinhaltet komplexes Datenverständnis und Rendering, und die meisten sogenannten "freien" Tools auf dem Markt haben schlechte Erfahrung. Es wird empfohlen, Computer-Seiten-Tools zu verwenden oder Cloud-Dienste zu verwenden oder Apps selbst zu entwickeln, um zuverlässigere Conversion-Effekte zu erhalten.

Die Geschwindigkeit der mobilen XML zu PDF hängt von den folgenden Faktoren ab: der Komplexität der XML -Struktur. Konvertierungsmethode für mobile Hardware-Konfiguration (Bibliothek, Algorithmus) -Codierungsoptimierungsmethoden (effiziente Bibliotheken, Optimierung von Algorithmen, Cache-Daten und Nutzung von Multi-Threading). Insgesamt gibt es keine absolute Antwort und es muss gemäß der spezifischen Situation optimiert werden.

Es ist nicht einfach, XML direkt auf Ihr Telefon in PDF umzuwandeln, kann jedoch mit Hilfe von Cloud -Diensten erreicht werden. Es wird empfohlen, eine leichte mobile App zu verwenden, um XML -Dateien hochzuladen und generierte PDFs zu empfangen und sie mit Cloud -APIs zu konvertieren. Cloud -APIs verwenden serverlose Computerdienste, und die Auswahl der richtigen Plattform ist entscheidend. Bei der Behandlung von XML -Parsen und PDF -Generation müssen Komplexität, Fehlerbehebung, Sicherheit und Optimierungsstrategien berücksichtigt werden. Der gesamte Prozess erfordert, dass die Front-End-App und die Back-End-API zusammenarbeiten, und es erfordert ein gewisses Verständnis einer Vielzahl von Technologien.

Um XML -Bilder zu konvertieren, müssen Sie zuerst die XML -Datenstruktur ermitteln, dann eine geeignete grafische Bibliothek (z. B. Pythons Matplotlib) und eine Methode auswählen, eine Visualisierungsstrategie basierend auf der Datenstruktur auswählen, das Datenvolumen und das Bildformat, die Batch -Verarbeitung ausführen oder effiziente Bibliotheken verwenden und schließlich als PNG, JPEG oder SVG nach den Bedürfnissen speichern.
