ActiveState Code

Recipe 157035: tail -f in Python


A simple implementation of the standard UNIX utility tail -f in Python.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import time

while 1:
    where = file.tell()
    line = file.readline()
    if not line:
        time.sleep(1)
        file.seek(where)
    else:
        print line, # already has newline

Discussion

The recipe works by line, and actually differs from standard tail -f in that at startup it does not do the equivalent of a standard tail (that is, it will show what is appended to the file after it starts getting run, but that is all).

Comments

  1. 1. At 9:27 a.m. on 2 jan 2003, Kirk Reeves said:

    How to make it usable.

    import time, os
    
    #Set the filename and open the file
    filename = 'security_log'
    file = open(filename,'r')
    
    #Find the size of the file and move to the end
    st_results = os.stat(filename)
    st_size = st_results[6]
    file.seek(st_size)
    
    while 1:
        where = file.tell()
        line = file.readline()
        if not line:
            time.sleep(1)
            file.seek(where)
        else:
            print line, # already has newline
    
  2. 2. At 6:02 a.m. on 26 may 2003, Ed Pascoe said:

    What tail-10 would do. Sometimes you actually just want the last 10 lines of the file. This will work quite happily on 10Meg files.

    I would love to know if there is a more efficient method.

    import sys
    def tail_lines(filename,linesback=10,returnlist=0):
        """Does what "tail -10 filename" would have done
           Parameters:
                filename   file to read
                linesback  Number of lines to read from end of file
                returnlist Return a list containing the lines instead of a string
    
        """
        avgcharsperline=75
    
        file = open(filename,'r')
        while 1:
            try: file.seek(-1 * avgcharsperline * linesback,2)
            except IOError: file.seek(0)
            if file.tell() == 0: atstart=1
            else: atstart=0
    
            lines=file.read().split("\n")
            if (len(lines) > (linesback+1)) or atstart: break
            #The lines are bigger than we thought
            avgcharsperline=avgcharsperline * 1.3 #Inc avg for retry
        file.close()
    
        if len(lines) > linesback: start=len(lines)-linesback -1
        else: start=0
        if returnlist: return lines[start:len(lines)-1]
    
        out=""
        for l in lines[start:len(lines)-1]: out=out + l + "\n"
        return out
    
    print tail_lines('/etc/hosts',5,1)
    sys.stdout.write(tail_lines('/etc/hosts',5))
    
  3. 3. At 4:10 p.m. on 29 jul 2003, Graham Nicholls said:

    Err, unless I'm missing something. This seems rather inefficient - suppose several lines are added to the file at once - this version will read them a line at a time; pausing for a second between each.

    I'm doing more or less the same, but seeking to EOF then using readlines: infile,seek(0,2) while 1: lines=infile.readlines if not lines: time.sleep(1) else

    print the line, or pattern match in it, etc.

    Better? (New to Python, so I could be way off the mark!)

  4. 4. At 5:40 p.m. on 30 nov 2007, Sam Jansen said:

    A simple addition... A nice addition to this is to use "yield" to make the code generic. That is,

    def tail_f(file):
      interval = 1.0
    
      while True:
        where = file.tell()
        line = file.readline()
        if not line:
          time.sleep(interval)
          file.seek(where)
        else:
          yield line
    

    Which then allows you to write code like:

    for line in tail_f(open(sys.argv[1])):
      print line,
    

Sign in to comment