ybnormal has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I'm having a problem to manipulate the file pointer on STDOUT, and I'd like to hear from experienced people. Here is a sample code that replaces 1 from the left with 0 every second, and this works.
use strict; use warnings; $|=1; open(F, '+< temp'); print F "11111111111111111111111111111111\n"; for (0..31){ seek(F, $_, 0); print F '0'; sleep(1); } close(F);
Below is the sample oode that I have a problem with. It is aimed to do the same as above program except doing it on STDOUT, but seek() doesn't seem to move the file pointer and it starts printing '0' from where the current pointer is, instead of overriding existing strings.
use strict; use warnings; $|=1; open(STDOUT, '+<'); print STDOUT "11111111111111111111111111111111\n"; for (0..31){ seek(STDOUT, $_, 0); print STDOUT '0'; sleep(1); } close(STDOUT);
Does seek() not work on STDOUT in the same way as it does on the files, or am I doing something wrong ? Appreciate your help.

Replies are listed 'Best First'.
Re: seek() command on STDOUT
by BrowserUk (Patriarch) on Apr 30, 2010 at 05:11 UTC
    Does seek() not work on STDOUT

    Short answer:No. Longer answer: No. How could it?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      There is nothing special about STDOUT, it's just another file handle. seek() does work on STDOUT if STDOUT is seekable, e.g. a plain file. But (pseudo) TTYs usually aren't seekable, at least not for writing, so seek() doesn't work when STDOUT is not redirected.

      #!/usr/bin/perl -w use strict; seek(STDOUT,14,0); print "Now I'm there\n"; seek(STDOUT,0,0); print "Now I'm here\n";

      Run without redirection (perl foo.pl), and you get:

      Now I'm there Now I'm here

      The seek()s simply fail, and because the code does not check for errors, seek() seems to be a dummy for STDOUT.

      Run again, redirecting to a file (perl foo.pl > foo.txt; cat foo.txt), and the file contains:

      Now I'm here Now I'm there

      Now let's play safe and add some error checking:

      #!/usr/bin/perl -w use strict; seek(STDOUT,14,0) or warn $!; print "Now I'm there\n"; seek(STDOUT,0,0) or warn $!; print "Now I'm here\n";

      Run again, without redirection:

      Illegal seek at foo.pl line 4. Now I'm there Illegal seek at foo.pl line 6. Now I'm here

      So, seek() is not a dummy, it simply fails, because the TTY does not allow seek() for write access.

      Redirecting to a file, as above, works without any warnings:

      Now I'm here Now I'm there

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        There is nothing special about STDOUT, it's just another file handle.

        Yes there is. By default, its pre-opened--and in most sensible uses--is connected to either the console or a pipe. Both non-seekable devices.

        Yes, the OP could re-open STDOUT to a seekable device, (he could also overload '+' to do subtraction), but if he was doing that, he wouldn't have a problem.

Re: seek() command on STDOUT
by cdarke (Prior) on Apr 30, 2010 at 08:52 UTC
    There are several problems with your code, chief among them is that you are not checking any of the IO calls. I altered it to:
    use strict; use warnings; $|=1; open(STDOUT, '+<') or die "open: $!"; print STDOUT "11111111111111111111111111111111\n" or die "print 1: $!" +; for (0..31){ seek(STDOUT, $_, 0) or die "seek: $!"; print STDOUT '0' or die "print 0: $!"; sleep(1); } close(STDOUT);
    This fails with open: No such file or directory.
    Although you can open an existing file handle as another, as others have said, it still won't work. Changing the open:
    open(STDOUT, '+<', "&STDOUT") or die "open: $!";
    now fails on the seek: seek: Invalid seek because it is not a seekable device.
Re: seek() command on STDOUT
by afoken (Chancellor) on Apr 30, 2010 at 15:13 UTC

    After reading the posting again and again, I think that you really search for a progress bar or something like that. You want to move the cursor and change a character on the (virtual) terminal. And seek just don't let you do that. Am I right?

    If yes, you are searching for information about terminal control codes. One of the most primitive ones is the backspace, and it works on nearly every terminal. Stupid example:

    #!/usr/bin/perl -w use strict; $|=1; my @spinner=qw( / - \ | ); print "Wait ... "; for my $i (0..19) { print "$spinner[$i % 4]\b"; sleep 1; } print "- done\n";

    There are much more terminal control codes. Unless you work with exotic hardware or exotic operating systems, the generic VT100 or xterm codes will work. There are libraries that take care of the actual codes, like termcap and the newer http://en.wikipedia.org/wiki/Terminfo. Libraries like curses and the newer ncurses add functions for text mode user interfaces. You can get Perl interfaces for all of those libraries on CPAN.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      Thanks,

      The program I'm writing is a simple terminal game called snake that was a popular arcade game in the '70s. It has 50x50 grids where the snake-like creature moves around and the player steers it not to hit the blockage or itself. It is intended to run on xterm.

      I finished the program, but currently, it refreshes the display every second(and getting faster over the time) with print `clear` and redraws the entire 50x50 grids with the new pattern. However, since all the grids are refreshed every time, the flickering is annoying especially when the snake's move gets faster. Then I realized that only 2 out of 50x50 grids are required to be updated every time(The head and tailend of the snake need to be updated to move forward) and I need to move the cursor to those 2 locations to update it every time. I thought seek() could do that on STDOUT where I got it wrong.

      So, basically, like you said, I need to move the cursor to the random points of 50x50 grid on xterm, update them with keeping all the other grids unchanged.

      Is ncurses able to do it ?

      Thanks for your help..

        On *nix, you can probably use ansi escape sequences to move the cursor around (untested):

        sub moveTo{ my( $x, $y ) = @_; return chr(27) . "[$y;$xH"; } print moveTo( 25, 25), 'some text'; print moveTo( 10, 10 ), ' ';

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.