Create a gist now

Instantly share code, notes, and snippets.

@amitsaha /tail.py
Last active Aug 22, 2017

What would you like to do?
Simple implementation of the tail command in Python
'''
Basic tail command implementation
Usage:
tail.py filename numlines
'''
import sys
import linecache
if len(sys.argv) !=3:
print 'Usage: tail.py <file> <nlines>'
sys.exit(1)
# filename and number of lines requested
fname, nlines = sys.argv[1:]
nlines = int(nlines)
# count the total number of lines
tot_lines = len(open(fname).readlines())
# use line cache module to read the lines
for i in range(tot_lines - nlines + 1, tot_lines+1):
print linecache.getline(sys.argv[1],i),
""" This is a more efficient version, since it does not read the entire
file
"""
import sys
import os
bufsize = 8192
lines = int(sys.argv[1])
fname = sys.argv[2]
fsize = os.stat(fname).st_size
iter = 0
with open(sys.argv[2]) as f:
if bufsize > fsize:
bufsize = fsize-1
data = []
while True:
iter +=1
f.seek(fsize-bufsize*iter)
data.extend(f.readlines())
if len(data) >= lines or f.tell() == 0:
print(''.join(data[-lines:]))
break

Excellent implementation! Simple and elegant. Well done man!

There's a couple of problems in the second implementation when you exceed the buffer size. f.tell() won't ever == 0 since it's after f.readlines(), and data isn't cleared before f.readlines() so you end up with duplicated data.

ksingh7 commented Jan 23, 2016

Another way of doing it

#!/usr/bin/python

import sys

if len(sys.argv) !=3:
    print 'Usage: tail.py <file> <nlines>'
    sys.exit(1)

fname, nlines = sys.argv[1:]
num_lines = int(nlines)

with open(fname) as f:
    content = f.read().splitlines()

count = len(content)
for i in range(count-num_lines,count):
  print content[i]

Kentzo commented Feb 23, 2016

And yet another way of doing this: tailhead and pytailer.

mikewen commented Feb 29, 2016

use deque:
from collections import deque
print deque(open(filename), nLines)

rodmur commented Apr 7, 2017

For what it's worth, I adapted some code from here, basically just using seek() and read() one at time to read backwards and count newlines, that way you don't need a buffer.

#!/usr/bin/python3

import os,sys

def tail_file(filename, nlines):
    with open(filename) as qfile:
        qfile.seek(0, os.SEEK_END)
        endf = position = qfile.tell()
        linecnt = 0
        while position >= 0:
            qfile.seek(position)
            next_char = qfile.read(1)
            if next_char == "\n" and position != endf-1:
                linecnt += 1

            if linecnt == nlines:
                break
            position -= 1

        if position < 0:
            qfile.seek(0)

        print(qfile.read(),end='')


if __name__ == '__main__':
    filename = sys.argv[1]
    nlines = int(sys.argv[2])
    tail_file(filename, nlines)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment