it-swarm.com.de

Holen Sie sich die letzten n Zeilen einer Datei mit Python, ähnlich wie tail

Ich schreibe einen Protokolldatei-Viewer für eine Webanwendung, und dafür möchte ich durch die Zeilen der Protokolldatei blättern. Die Elemente in der Datei basieren auf der neuesten Zeile am unteren Rand.

Ich brauche also eine tail()-Methode, die n Zeilen von unten lesen kann und einen Versatz unterstützt. Was ich mir so vorgestellt habe, sieht so aus:

def tail(f, n, offset=0):
    """Reads a n lines from f with an offset of offset lines."""
    avg_line_length = 74
    to_read = n + offset
    while 1:
        try:
            f.seek(-(avg_line_length * to_read), 2)
        except IOError:
            # woops.  apparently file is smaller than what we want
            # to step back, go to the beginning instead
            f.seek(0)
        pos = f.tell()
        lines = f.read().splitlines()
        if len(lines) >= to_read or pos == 0:
            return lines[-to_read:offset and -offset or None]
        avg_line_length *= 1.3

Ist das ein vernünftiger Ansatz? Was ist die empfohlene Methode, um Protokolldateien mit Offsets zu versehen?

162
Armin Ronacher

Der Code, den ich verwendet habe. Ich denke, das ist bisher das Beste:

def tail(f, n, offset=None):
    """Reads a n lines from f with an offset of offset lines.  The return
    value is a Tuple in the form ``(lines, has_more)`` where `has_more` is
    an indicator that is `True` if there are more lines in the file.
    """
    avg_line_length = 74
    to_read = n + (offset or 0)

    while 1:
        try:
            f.seek(-(avg_line_length * to_read), 2)
        except IOError:
            # woops.  apparently file is smaller than what we want
            # to step back, go to the beginning instead
            f.seek(0)
        pos = f.tell()
        lines = f.read().splitlines()
        if len(lines) >= to_read or pos == 0:
            return lines[-to_read:offset and -offset or None], \
                   len(lines) > to_read or pos > 0
        avg_line_length *= 1.3
19
Armin Ronacher

Dies kann schneller sein als bei Ihnen. Macht keine Annahmen über die Leitungslänge. Blättert Block für Block durch die Datei, bis die richtige Anzahl von\n-Zeichen gefunden wurde.

def tail( f, lines=20 ):
    total_lines_wanted = lines

    BLOCK_SIZE = 1024
    f.seek(0, 2)
    block_end_byte = f.tell()
    lines_to_go = total_lines_wanted
    block_number = -1
    blocks = [] # blocks of size BLOCK_SIZE, in reverse order starting
                # from the end of the file
    while lines_to_go > 0 and block_end_byte > 0:
        if (block_end_byte - BLOCK_SIZE > 0):
            # read the last block we haven't yet read
            f.seek(block_number*BLOCK_SIZE, 2)
            blocks.append(f.read(BLOCK_SIZE))
        else:
            # file too small, start from begining
            f.seek(0,0)
            # only read what was not read
            blocks.append(f.read(block_end_byte))
        lines_found = blocks[-1].count('\n')
        lines_to_go -= lines_found
        block_end_byte -= BLOCK_SIZE
        block_number -= 1
    all_read_text = ''.join(reversed(blocks))
    return '\n'.join(all_read_text.splitlines()[-total_lines_wanted:])

Ich mag keine kniffligen Annahmen über die Leitungslänge, wenn man so etwas praktisch nie wissen kann.

Im Allgemeinen werden dadurch die letzten 20 Zeilen im ersten oder zweiten Durchlauf durch die Schleife lokalisiert. Wenn Ihre Sache mit 74 Zeichen tatsächlich genau ist, erstellen Sie die Blockgröße 2048, und Sie werden fast sofort 20 Zeilen geschnitten.

Außerdem verbrenne ich nicht viele Gehirnkalorien, die versuchen, die Ausrichtung mit physischen Betriebssystemblöcken zu verfeinern. Ich bezweifle, dass Sie bei Verwendung dieser übergeordneten E/A-Pakete jegliche Auswirkungen auf die Leistung haben, wenn Sie versuchen, sich an OS-Blockgrenzen auszurichten. Wenn Sie untergeordnete E/A verwenden, wird möglicherweise eine Beschleunigung angezeigt.

115
S.Lott

Nimmt an, dass ein Unix-ähnliches System unter Python 2 möglich ist:

import os
def tail(f, n, offset=0):
  stdin,stdout = os.popen2("tail -n "+n+offset+" "+f)
  stdin.close()
  lines = stdout.readlines(); stdout.close()
  return lines[:,-offset]

Für Python 3 können Sie Folgendes tun:

import subprocess
def tail(f, n, offset=0):
    proc = subprocess.Popen(['tail', '-n', n + offset, f], stdout=subprocess.PIPE)
    lines = proc.stdout.readlines()
    return lines[:, -offset]
73
Mark

Wenn die gesamte Datei gelesen werden kann, verwenden Sie eine Deque.

from collections import deque
deque(f, maxlen=n)

Vor 2.6 gab es für deques keine Maxlen-Option, die Implementierung ist jedoch leicht genug.

import itertools
def maxque(items, size):
    items = iter(items)
    q = deque(itertools.islice(items, size))
    for item in items:
        del q[0]
        q.append(item)
    return q

Wenn Sie die Datei am Ende lesen möchten, verwenden Sie eine Galopp-Suche (a.k.a exponentiell).

def tail(f, n):
    assert n >= 0
    pos, lines = n+1, []
    while len(lines) <= n:
        try:
            f.seek(-pos, 2)
        except IOError:
            f.seek(0)
            break
        finally:
            lines = list(f)
        pos *= 2
    return lines[-n:]
29
A. Coady

Die Antwort von S.Lott oben funktioniert fast für mich, gibt mir aber teilweise Zeilen. Es stellt sich heraus, dass Daten an Blockgrenzen beschädigt werden, da Daten die gelesenen Blöcke in umgekehrter Reihenfolge enthalten. Wenn '' .join (Daten) aufgerufen wird, sind die Blöcke in der falschen Reihenfolge. Das behebt das.

def tail(f, window=20):
    """
    Returns the last `window` lines of file `f` as a list.
    f - a byte file-like object
    """
    if window == 0:
        return []
    BUFSIZ = 1024
    f.seek(0, 2)
    bytes = f.tell()
    size = window + 1
    block = -1
    data = []
    while size > 0 and bytes > 0:
        if bytes - BUFSIZ > 0:
            # Seek back one whole BUFSIZ
            f.seek(block * BUFSIZ, 2)
            # read BUFFER
            data.insert(0, f.read(BUFSIZ))
        else:
            # file too small, start from begining
            f.seek(0,0)
            # only read what was not read
            data.insert(0, f.read(bytes))
        linesFound = data[0].count('\n')
        size -= linesFound
        bytes -= BUFSIZ
        block -= 1
    return ''.join(data).splitlines()[-window:]
26
papercrane

Hier ist meine Antwort. Python pur Mit Timeit scheint es ziemlich schnell zu sein. Tailing von 100 Zeilen einer Protokolldatei mit 100.000 Zeilen:

>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=10)
0.0014600753784179688
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=100)
0.00899195671081543
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=1000)
0.05842900276184082
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=10000)
0.5394978523254395
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=100000)
5.377126932144165

Hier ist der Code:

import os


def tail(f, lines=1, _buffer=4098):
    """Tail a file and get X lines from the end"""
    # place holder for the lines found
    lines_found = []

    # block counter will be multiplied by buffer
    # to get the block size from the end
    block_counter = -1

    # loop until we find X lines
    while len(lines_found) < lines:
        try:
            f.seek(block_counter * _buffer, os.SEEK_END)
        except IOError:  # either file is too small, or too many lines requested
            f.seek(0)
            lines_found = f.readlines()
            break

        lines_found = f.readlines()

        # we found enough lines, get out
        # Removed this line because it was redundant the while will catch
        # it, I left it for history
        # if len(lines_found) > lines:
        #    break

        # decrement the block counter to get the
        # next X bytes
        block_counter -= 1

    return lines_found[-lines:]
24
glenbot

Einfache und schnelle Lösung mit mmap:

import mmap
import os

def tail(filename, n):
    """Returns last n lines from the filename. No exception handling"""
    size = os.path.getsize(filename)
    with open(filename, "rb") as f:
        # for Windows the mmap parameters are different
        fm = mmap.mmap(f.fileno(), 0, mmap.MAP_SHARED, mmap.PROT_READ)
        try:
            for i in xrange(size - 1, -1, -1):
                if fm[i] == '\n':
                    n -= 1
                    if n == -1:
                        break
            return fm[i + 1 if i else 0:].splitlines()
        finally:
            fm.close()
13
dimitri

Eine noch sauberere python3-kompatible Version, die nicht eingefügt, sondern angehängt und umgekehrt wird:

def tail(f, window=1):
    """
    Returns the last `window` lines of file `f` as a list of bytes.
    """
    if window == 0:
        return b''
    BUFSIZE = 1024
    f.seek(0, 2)
    end = f.tell()
    nlines = window + 1
    data = []
    while nlines > 0 and end > 0:
        i = max(0, end - BUFSIZE)
        nread = min(end, BUFSIZE)

        f.seek(i)
        chunk = f.read(nread)
        data.append(chunk)
        nlines -= chunk.count(b'\n')
        end -= nread
    return b'\n'.join(b''.join(reversed(data)).splitlines()[-window:])

benutze es so:

with open(path, 'rb') as f:
    last_lines = tail(f, 3).decode('utf-8')
4
Hauke Rehfeld

Antwort auf Veranlassung von Kommentatoren zu meine Antwort auf eine ähnliche Frage wo dieselbe Technik verwendet wurde, um die letzte Zeile einer Datei zu mutieren, anstatt sie nur zu bekommen.

Für eine Datei von erheblicher Größe ist mmap der beste Weg, dies zu tun. Um die vorhandene mmap-Antwort zu verbessern, ist diese Version zwischen Windows und Linux portierbar und sollte schneller ausgeführt werden (obwohl sie ohne einige Änderungen an 32-Bit-Python mit Dateien im GB-Bereich nicht funktioniert. Weitere Hinweise finden Sie in der anderen Antwort.) und wie man damit umgeht, um mit Python 2 zu arbeiten ).

import io  # Gets consistent version of open for both Py2.7 and Py3.x
import itertools
import mmap

def skip_back_lines(mm, numlines, startidx):
    '''Factored out to simplify handling of n and offset'''
    for _ in itertools.repeat(None, numlines):
        startidx = mm.rfind(b'\n', 0, startidx)
        if startidx < 0:
            break
    return startidx

def tail(f, n, offset=0):
    # Reopen file in binary mode
    with io.open(f.name, 'rb') as binf, mmap.mmap(binf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
        # len(mm) - 1 handles files ending w/newline by getting the prior line
        startofline = skip_back_lines(mm, offset, len(mm) - 1)
        if startofline < 0:
            return []  # Offset lines consumed whole file, nothing to return
            # If using a generator function (yield-ing, see below),
            # this should be a plain return, no empty list

        endoflines = startofline + 1  # Slice end to omit offset lines

        # Find start of lines to capture (add 1 to move from newline to beginning of following line)
        startofline = skip_back_lines(mm, n, startofline) + 1

        # Passing True to splitlines makes it return the list of lines without
        # removing the trailing newline (if any), so list mimics f.readlines()
        return mm[startofline:endoflines].splitlines(True)
        # If Windows style \r\n newlines need to be normalized to \n, and input
        # is ASCII compatible, can normalize newlines with:
        # return mm[startofline:endoflines].replace(os.linesep.encode('ascii'), b'\n').splitlines(True)

Dies setzt voraus, dass die Anzahl der Zeilen begrenzt ist, die Sie alle auf einmal sicher in den Speicher lesen können. Sie können dies auch zu einer Generatorfunktion machen und manuell eine Zeile lesen, indem Sie die letzte Zeile durch Folgendes ersetzen:

        mm.seek(startofline)
        # Call mm.readline n times, or until EOF, whichever comes first
        # Python 3.2 and earlier:
        for line in itertools.islice(iter(mm.readline, b''), n):
            yield line

        # 3.3+:
        yield from itertools.islice(iter(mm.readline, b''), n)

Zuletzt lesen Sie diese im Binärmodus (für die Verwendung von mmap erforderlich), sodass Sie str-Zeilen (Py2) und bytes-Zeilen (Py3) erhalten. Wenn Sie unicode (Py2) oder str (Py3) möchten, könnte der iterative Ansatz angepasst werden, um für Sie zu decodieren und/oder Zeilenumbrüche zu korrigieren:

        lines = itertools.islice(iter(mm.readline, b''), n)
        if f.encoding:  # Decode if the passed file was opened with a specific encoding
            lines = (line.decode(f.encoding) for line in lines)
        if 'b' not in f.mode:  # Fix line breaks if passed file opened in text mode
            lines = (line.replace(os.linesep, '\n') for line in lines)
        # Python 3.2 and earlier:
        for line in lines:
            yield line
        # 3.3+:
        yield from lines

Hinweis: Ich habe das alles auf einem Computer eingegeben, auf dem ich keinen Zugriff auf Python habe, um zu testen. Bitte lass es mich wissen, wenn ich etwas getippt habe; Dies war ziemlich ähnlich zu meiner anderen Antwort dass ich denke sollte es funktionieren, aber die Tweaks (z. B. die Behandlung eines offset) könnten zu subtilen Fehlern führen. Bitte lassen Sie mich in den Kommentaren wissen, ob es Fehler gibt.

3
ShadowRanger

Ich fand den Popen oben als die beste Lösung. Es ist schnell und schmutzig und funktioniert Für Python 2.6 auf Unix-Computern habe ich Folgendes verwendet

    def GetLastNLines(self, n, fileName):
    """
    Name:           Get LastNLines
    Description:        Gets last n lines using Unix tail
    Output:         returns last n lines of a file
    Keyword argument:
    n -- number of last lines to return
    filename -- Name of the file you need to tail into
    """
    p=subprocess.Popen(['tail','-n',str(n),self.__fileName], stdout=subprocess.PIPE)
    soutput,sinput=p.communicate()
    return soutput

soutput wird die letzten n Zeilen des Codes enthalten. soutput Zeile für Zeile durchlaufen:

for line in GetLastNLines(50,'myfile.log').split('\n'):
    print line
3
Marko

Aktualisieren Sie die @papercrane-Lösung auf python3 . Öffnen Sie die Datei mit open(filename, 'rb') und:

def tail(f, window=20):
    """Returns the last `window` lines of file `f` as a list.
    """
    if window == 0:
        return []

    BUFSIZ = 1024
    f.seek(0, 2)
    remaining_bytes = f.tell()
    size = window + 1
    block = -1
    data = []

    while size > 0 and remaining_bytes > 0:
        if remaining_bytes - BUFSIZ > 0:
            # Seek back one whole BUFSIZ
            f.seek(block * BUFSIZ, 2)
            # read BUFFER
            bunch = f.read(BUFSIZ)
        else:
            # file too small, start from beginning
            f.seek(0, 0)
            # only read what was not read
            bunch = f.read(remaining_bytes)

        bunch = bunch.decode('utf-8')
        data.insert(0, bunch)
        size -= bunch.count('\n')
        remaining_bytes -= BUFSIZ
        block -= 1

    return ''.join(data).splitlines()[-window:]
3
Emilio

Hier ist eine ziemlich einfache Implementierung:

with open('/etc/passwd', 'r') as f:
  try:
    f.seek(0,2)
    s = ''
    while s.count('\n') < 11:
      cur = f.tell()
      f.seek((cur - 10))
      s = f.read(10) + s
      f.seek((cur - 10))
    print s
  except Exception as e:
    f.readlines()
2
GL2014

basierend auf der von S.Lott am besten bewerteten Antwort (25. September 08 um 21:43), jedoch für kleine Dateien korrigiert.

def tail(the_file, lines_2find=20):  
    the_file.seek(0, 2)                         #go to end of file
    bytes_in_file = the_file.tell()             
    lines_found, total_bytes_scanned = 0, 0
    while lines_2find+1 > lines_found and bytes_in_file > total_bytes_scanned: 
        byte_block = min(1024, bytes_in_file-total_bytes_scanned)
        the_file.seek(-(byte_block+total_bytes_scanned), 2)
        total_bytes_scanned += byte_block
        lines_found += the_file.read(1024).count('\n')
    the_file.seek(-total_bytes_scanned, 2)
    line_list = list(the_file.readlines())
    return line_list[-lines_2find:]

    #we read at least 21 line breaks from the bottom, block by block for speed
    #21 to ensure we don't get a half line

Hoffe das ist nützlich.

2
Eyecue

Es gibt bereits einige Implementierungen von tail on pypi, die Sie mit pip installieren können: 

  • mtFileUtil
  • multitail
  • log4tailer
  • ...

Je nach Ihrer Situation kann die Verwendung eines dieser vorhandenen Tools Vorteile bieten.

2
Travis Bear

sie können mit f.seek (0, 2) zum Ende Ihrer Datei gehen und dann die Zeilen nacheinander ablesen, wobei readline () wie folgt ersetzt wird:

def readline_backwards(self, f):
    backline = ''
    last = ''
    while not last == '\n':
        backline = last + backline
        if f.tell() <= 0:
            return backline
        f.seek(-1, 1)
        last = f.read(1)
        f.seek(-1, 1)
    backline = last
    last = ''
    while not last == '\n':
        backline = last + backline
        if f.tell() <= 0:
            return backline
        f.seek(-1, 1)
        last = f.read(1)
        f.seek(-1, 1)
    f.seek(1, 1)
    return backline
1
rabbit

Aus Gründen der Effizienz bei sehr großen Dateien (häufig in Logfile-Situationen, in denen Sie möglicherweise tail verwenden möchten) möchten Sie im Allgemeinen das Lesen der gesamten Datei vermeiden (auch wenn Sie dies tun, ohne die gesamte Datei auf einmal in den Speicher zu lesen) Sie müssen den Versatz in Zeilen und nicht in Zeichen berechnen. Eine Möglichkeit ist das Rückwärtslesen mit seek () char von char, aber das ist sehr langsam. Stattdessen ist es besser, größere Blöcke zu verarbeiten.

Ich habe eine Utility-Funktion, die ich vor einiger Zeit geschrieben habe, um Dateien rückwärts zu lesen, die hier verwendet werden können.

import os, itertools

def rblocks(f, blocksize=4096):
    """Read file as series of blocks from end of file to start.

    The data itself is in normal order, only the order of the blocks is reversed.
    ie. "hello world" -> ["ld","wor", "lo ", "hel"]
    Note that the file must be opened in binary mode.
    """
    if 'b' not in f.mode.lower():
        raise Exception("File must be opened using binary mode.")
    size = os.stat(f.name).st_size
    fullblocks, lastblock = divmod(size, blocksize)

    # The first(end of file) block will be short, since this leaves 
    # the rest aligned on a blocksize boundary.  This may be more 
    # efficient than having the last (first in file) block be short
    f.seek(-lastblock,2)
    yield f.read(lastblock)

    for i in range(fullblocks-1,-1, -1):
        f.seek(i * blocksize)
        yield f.read(blocksize)

def tail(f, nlines):
    buf = ''
    result = []
    for block in rblocks(f):
        buf = block + buf
        lines = buf.splitlines()

        # Return all lines except the first (since may be partial)
        if lines:
            result.extend(lines[1:]) # First line may not be complete
            if(len(result) >= nlines):
                return result[-nlines:]

            buf = lines[0]

    return ([buf]+result)[-nlines:]


f=open('file_to_tail.txt','rb')
for line in tail(f, 20):
    print line

[Bearbeiten] Spezifischere Version hinzugefügt (Umkehrung muss nicht zweimal durchgeführt werden)

1
Brian

Einfach:

with open("test.txt") as f:
data = f.readlines()
tail = data[-2:]
print(''.join(tail)
1

Basierend auf der Antwort von Eyecue (10. Juni 10 um 21:28): Diese Klasse fügt die Methode head () und tail () dem Dateiobjekt hinzu.

class File(file):
    def head(self, lines_2find=1):
        self.seek(0)                            #Rewind file
        return [self.next() for x in xrange(lines_2find)]

    def tail(self, lines_2find=1):  
        self.seek(0, 2)                         #go to end of file
        bytes_in_file = self.tell()             
        lines_found, total_bytes_scanned = 0, 0
        while (lines_2find+1 > lines_found and
               bytes_in_file > total_bytes_scanned): 
            byte_block = min(1024, bytes_in_file-total_bytes_scanned)
            self.seek(-(byte_block+total_bytes_scanned), 2)
            total_bytes_scanned += byte_block
            lines_found += self.read(1024).count('\n')
        self.seek(-total_bytes_scanned, 2)
        line_list = list(self.readlines())
        return line_list[-lines_2find:]

Verwendungszweck:

f = File('path/to/file', 'r')
f.head(3)
f.tail(3)
1
fdb

Einige dieser Lösungen haben Probleme, wenn die Datei nicht mit\n endet oder das vollständige Lesen der ersten Zeile sichergestellt ist.

def tail(file, n=1, bs=1024):
    f = open(file)
    f.seek(-1,2)
    l = 1-f.read(1).count('\n') # If file doesn't end in \n, count it anyway.
    B = f.tell()
    while n >= l and B > 0:
            block = min(bs, B)
            B -= block
            f.seek(B, 0)
            l += f.read(block).count('\n')
    f.seek(B, 0)
    l = min(l,n) # discard first (incomplete) line if l > n
    lines = f.readlines()[-l:]
    f.close()
    return lines
1
David Rogers

Update für Antwort von A.Coady

Funktioniert mit Python 3 .

Dies verwendet Exponential Search und puffert nur N Zeilen von hinten und ist sehr effizient.

import time
import os
import sys

def tail(f, n):
    assert n >= 0
    pos, lines = n+1, []

    # set file pointer to end

    f.seek(0, os.SEEK_END)

    isFileSmall = False

    while len(lines) <= n:
        try:
            f.seek(f.tell() - pos, os.SEEK_SET)
        except ValueError as e:
            # lines greater than file seeking size
            # seek to start
            f.seek(0,os.SEEK_SET)
            isFileSmall = True
        except IOError:
            print("Some problem reading/seeking the file")
            sys.exit(-1)
        finally:
            lines = f.readlines()
            if isFileSmall:
                break

        pos *= 2

    print(lines)

    return lines[-n:]




with open("stream_logs.txt") as f:
    while(True):
        time.sleep(0.5)
        print(tail(f,2))

0
Jigar Wala

Nicht das erste Beispiel mit einem Deque, sondern ein einfacheres. Dies ist allgemein: Es funktioniert für jedes iterierbare Objekt, nicht nur für eine Datei.

#!/usr/bin/env python
import sys
import collections
def tail(iterable, N):
    deq = collections.deque()
    for thing in iterable:
        if len(deq) >= N:
            deq.popleft()
        deq.append(thing)
    for thing in deq:
        yield thing
if __== '__main__':
    for line in tail(sys.stdin,10):
        sys.stdout.write(line)
0
Hal Canary
This is my version of tailf

import sys, time, os

filename = 'path to file'

try:
    with open(filename) as f:
        size = os.path.getsize(filename)
        if size < 1024:
            s = size
        else:
            s = 999
        f.seek(-s, 2)
        l = f.read()
        print l
        while True:
            line = f.readline()
            if not line:
                time.sleep(1)
                continue
            print line
except IOError:
    pass
0
Raj

Es gibt sehr nützliche module , die das können:

from file_read_backwards import FileReadBackwards

with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:

# getting lines by lines starting from the last line up
for l in frb:
    print(l)
0
Quinten Cabo

Ich musste einen bestimmten Wert aus der letzten Zeile einer Datei lesen und stolperte über diesen Thread. Anstatt das Rad in Python neu zu erfinden, erhielt ich ein kleines Shell-Skript, das als/usr/local/bin/get_last_netp gespeichert wurde:

#! /bin/bash
tail -n1 /home/leif/projects/transfer/export.log | awk {'print $14'}

Und im Python-Programm:

from subprocess import check_output

last_netp = int(check_output("/usr/local/bin/get_last_netp"))
0
Leifbk
import itertools
fname = 'log.txt'
offset = 5
n = 10
with open(fname) as f:
    n_last_lines = list(reversed([x for x in itertools.islice(f, None)][-(offset+1):-(offset+n+1):-1]))
0
Y Kal
import time

attemps = 600
wait_sec = 5
fname = "YOUR_PATH"

with open(fname, "r") as f:
    where = f.tell()
    for i in range(attemps):
        line = f.readline()
        if not line:
            time.sleep(wait_sec)
            f.seek(where)
        else:
            print line, # already has newline
0
moylop260
abc = "2018-06-16 04:45:18.68"
filename = "abc.txt"
with open(filename) as myFile:
    for num, line in enumerate(myFile, 1):
        if abc in line:
            lastline = num
print "last occurance of work at file is in "+str(lastline) 
0
Kant Manapure