Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python2
- """Mimics UNIX tail, especially its behavior when dealing with large files
- The I/O operations have nearly the same pattern as tail
- """
- import collections
- import sys
- import errno
- def tail_unseekable(filein, count):
- """Fallback for special files like pipes or terminals
- Arguments:
- filein -- file-like object opened for reading
- count -- number of lines to be written to stdout
- """
- sys.stdout.writelines(collections.deque(filein, count))
- def read_blocks(filein, positions, endpos):
- """Yields strings of 8192 bytes length from filein
- Arguments:
- filein -- seekable file opened for reading
- positions -- iterable of seek positions from which to read
- endpos -- a position beyond which shall not be read
- """
- blocks = ((blockpos, min(8192, endpos - blockpos)) for blockpos
- in positions)
- for blockpos, blocklen in blocks:
- filein.seek(blockpos)
- yield filein.read(blocklen)
- def count_lines(filein, count, endpos):
- """Reads the given file backwards, stopping after counting count lnes
- Arguments:
- filein -- seekable file opened for reading. After returning, the file
- position will be right after the returned data block
- count -- number of lines to find
- endpos -- a position beyond which shall not be read
- Returns:
- (linecount, blockdata)
- linecount -- the number of lines actually counted. This can be higher than
- count or lower (when the file is too short)
- blockdata -- the last block read from file, included in linecount
- """
- linecount = 0
- blockdata = None
- blockpositions = reversed(xrange(0, endpos, 8192))
- for blockdata in read_blocks(filein, blockpositions, endpos):
- blocklines = blockdata.count('\n')
- linecount += blocklines
- if linecount >= count:
- break
- return (linecount, blockdata)
- def print_partial(blockdata, actual_count, wanted_count):
- """Prints the tail of blockdata to stdout, skipping superfluous lines
- Arguments:
- blockdata -- a string containing lines
- actual_count -- number of lines in blockdata (and beyond)
- wanted_count -- number of lines that shall be written
- """
- toomuch = actual_count - wanted_count
- startpos = 0
- for _ in xrange(toomuch):
- startpos = blockdata.index('\n', startpos) + 1
- sys.stdout.write(buffer(blockdata, startpos))
- def print_to_pos(filein, endpos):
- """Prints filein to stdout from the current position to endpos"""
- blockpos = filein.tell()
- wholeblocks, remainder = divmod(endpos - blockpos, 8192)
- blocklens = [8192] * wholeblocks
- blocklens.append(remainder)
- blocks = (filein.read(blocklen) for blocklen in blocklens)
- for block in blocks:
- sys.stdout.write(block)
- # sys.stdout.writelines(blocks) # works, too, but buffers output
- def tail(filein, count):
- """Writes the last count lines from filein to stdout"""
- try:
- filein.seek(0, 2)
- except IOError as err:
- if err.errno == errno.ESPIPE:
- tail_unseekable(filein, count)
- return
- else:
- raise
- endpos = filein.tell()
- linecount, blockdata = count_lines(filein, count, endpos)
- print_partial(blockdata, linecount, count)
- print_to_pos(filein, endpos)
- def main():
- filename = sys.argv[1]
- count = 100
- with open(filename, 'r', 8192) as fd:
- tail(fd, count)
- return
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement