stdin - seek() issue on Linux.

Srihari Vijayaraghavan harisri at bigpond.com
Mon Apr 22 23:08:29 EDT 2002


Hello,

> Quoth "Terry Reedy" <tejarex at yahoo.com>:
> | "Srihari Vijayaraghavan" <no at spam.please> wrote in message
> | news:WMTw8.46875$uR5.106037 at newsfeeds.bigpond.com...

> |> I am a new-bie python user. I have Python 2.2.1 installed on a Linux
> |> computer. I get the following error message when I try to seek() in the
> |> stdin, like:
> |> #cat /var/log/messages | python test.py
> |> (where /var/log/messages is over 1MB in size)
> |>
> |> Traceback (most recent call last):
> |>   File "test.py", line 3, in ?
> |>     sys.stdin.seek(-512, 2)
> |> IOError: [Errno 29] Illegal seek
> |
> | By 'cat'ting the random access disk file, you turned it into a
> | forward-access-only stream of bytes.  You definitely cannot seek
> | backwards (and maybe not forward - don't know).  Instead, open() the
> | file and use the resulting file object:
> |
> | msgs = open("/var/log/messages", 'r')
> | msgs.seek(-512, 2)
> | ...
> 
> Right, though it can be fixed without changing test.py at all -
> "python test.py < /var/log/messages" will work fine.  This idiom
> ("cat file | ...") used to regularly receive a UUOC award, "Useless
> Use Of Cat", from notorious Perl hacker Randall Schwartz when I
> followed comp.unix.shell a few years back.
> 
> Of course if the real program uses some filter, instead of "cat",
> then there's no solution - you can't seek in a pipe, so the answer
> has to be "don't do that".
> 

Thanks for both of your responses. Very helpful.

My mistake, I haven't given the complete context. The above mentioned
method doesn't throw exceptions in Windows using the same Python
version (neither does it print anything, which is the ultimate problem
I would like to be solved).

Since I have already coded it in 'UUOC' :-) style, I thought it should
work even in Linux. But it looks like a wrong practice.

Are there any better method of redirecting the stdout of a program to
stdin of my program in both windows and linux? (specifically I should
be able to seek() on it)

Here is a sample program, where I am trying to use a simillar (bad)
idea:

### start
#!/usr/bin/python

# A python program to print tail portion of a file/stdin(not quite
yet)

import sys, getopt, xreadlines

def usage():
    print '''
    Usage: python tail.py -n<lines> myfile.txt yourfile.txt...
    (Where -n option is to specify the number of lines to be printed,
    and it defaults to 10 lines if not specified)

    If you do not specify any files, script will read from the
standard input.
    '''

def tail(stuff, lines):
    '''prints the specifed number of lines (if possible) from a given
file, stuff'''

    #print stuff.tell()
    stuff.seek(0, 2)
    #print stuff.tell()
    number_of_lines_read = 0

    while number_of_lines_read < lines:
        size = stuff.tell()
        if size >= 512:
            stuff.seek(-512, 1)
            position = stuff.tell()
            data = stuff.read(512)
            stuff.seek(position)
            number_of_lines_read = number_of_lines_read +
data.count('\n')
        elif size > 0 and size <= 511:
            stuff.seek(-1, 1)
            position = stuff.tell()
            data = stuff.read(1)
            stuff.seek(position)
            if data == '\n':
                number_of_lines_read = number_of_lines_read + 1
        elif size == 0:
            break

    if number_of_lines_read > lines:
        difference = number_of_lines_read - lines
        while difference:
            stuff.readline()
            difference = difference - 1

    #print stuff.tell()
    
    for everyline in xreadlines.xreadlines(stuff):
        print everyline,


def main():
    
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'n:h')
    except:
        usage()
        sys.exit()

    # let's default to 10 lines like GNU 'tail' utility
    lines = 10

    for opt, arg in opts:
        if opt in ('-n',): lines = int(arg)
        if opt in ('-h',):
            usage()
            sys.exit()

    if len(args) == 0:
        stuff = sys.stdin
        tail(stuff, lines)
        stuff.close()
    else:
        for everyfile in args:
            one_file = open(everyfile, 'r')
            tail(one_file, lines)
            one_file.close()


if __name__ == '__main__': main()
### End

If you enable the commented 'print' statements, you will know that
Python seek() successfully to various portions of the given file, for
eg, stdin or a file.

Thanks for your time.

Hari,
harisri at bigpond.com



More information about the Python-list mailing list