14

I'm lazy and I could write a script to do this, but I'm even too lazy to think of how to do it.

I often do things like :

cris$ python runexperiment.py > output.txt
cris$ cat output.txt

Sometimes when looking at the long output of an experiment I like to let the page just scroll and watch the successive patterns form and disperse. But using cat on a file with 1 million lines finishes in maybe 5 seconds. This is too quick even for me.

Is there any way that I can slow down the speed of viewing the file, something like a 'scroll utility'? I want fast, but not 200k lines a second (all of which presumably the display would never even register anyway).

Something like

cris$ scroll -lps=300 output.txt

And then sitting back and watching 300 lines per second roll past would be ideal, I imagine.

  • 7
    Try something like cat FILENAME | pv -l -L 900 -q. The limit is in bytes per second, not lines per second, so I'm making this a comment not an answer. – David Schwartz Dec 31 '12 at 7:54
  • Ok well that is a cool utility, and that works in part. But yes, it is a little choppy since it goes after bps not lps. – Cris Stringfellow Dec 31 '12 at 8:03
15

There are several utilities that let you specify a rate, like pv, but it's the rate in bytes per seconds, not lines per seconds.

But if you really want to use lps, you could do this:

perl -e 'print && select undef,undef,undef,.00333 while <>;'

Under perl, print while <> could be replaced by the -p switch:

perl -pe 'select undef,undef,undef,.00333'

Let's try:

time /bin/ls -l /usr/bin | perl -pe 'select undef,undef,undef,.00333' | wc
   2667   24902  171131

real    0m9.173s
user    0m0.056s
sys     0m0.048s

bc -l < <(echo 2667/9.173)
290.74457647443584432573

Explanation:

  • 300 lines / sec means 1 line by 0.0033333333 secs.

  • print without argument prints $_ which is default input space.

  • called as ... | perl -e, ... | perl -ne or ... | perl -pe, standard input would be automaticaly assigned to *STDIN which is default file descriptor, so <> would do the same as <STDIN> which will read from standard input until $/ (input record separator which is by default a newline) will be reached. In English, by default <> will read one line from standard input and assign content to $_ variable.

  • && is an and condition, but is used there as a chain command separator so after (successfully) print one line, doing next command.

  • select is a programmer's trick to not use sleep. This command is designed to trap events on file descriptors (inputs and/or outputs, files, socket and/or net sockets). With this command, a program could wait for 3 kind of events, feed ready to read, feed ready to write and some event happened on feed. The fourth argument is a timeout in seconds, so syntax is select <feeds where wait for input>, <feeds where having to write>, <feed where something could happen>, <timeout>.

For more precision, you could use Time::Hires perl module:

perl -MTime::HiRes -pe 'BEGIN{$start=Time::HiRes::time;$sleepPerLine=1/300};select undef,undef,undef,($start + $sleepPerLine*$. - Time::HiRes::time)'

Note: $. is current input line number.

Better written as cat >catLps.pl

#!/usr/bin/perl -w

use strict;
use Time::HiRes qw|time|;

my $start=time;
my $lps=300;

$lps=shift @ARGV if @ARGV && $ARGV[0]=~/^(\d+)$/;
my $sleepPerLine=1/$lps;

print &&
    select undef,undef,undef,($start + $sleepPerLine*$. - Time::HiRes::time)
    while <>

Usage:

catLps.pl [lps] [file] [file]...

First argument lps is optional line per seconds numeric argument (default: 300)

Note: if filename is only numeric, you may have to specifiy them with path: ./3.

Like cat this could pass files given as argument and/or standard input

So we could:

TIMEFORMAT='%R' 
time seq 1 100 | ./catLps.pl 100 >/dev/null 
1.040

time seq 1 10000 | ./catLps.pl 10000 >/dev/null  
1.042

For fun:

export TIMEFORMAT='%R' ;clear ;time seq 1 $((LINES-2)) | ./catLps.pl $((LINES-2))
  • 2
    that looks like some serious voodoo you are doing there. that is so cool, i tried it and it works. i have no idea how you did that though. what the hell is perl select ? undef? i can look it up. amazing. – Cris Stringfellow Dec 31 '12 at 8:18
  • 2
    @CrisStringfellow Ok, I've added some explanation and a full script using Time::HiRes perl module for more accuracy – F. Hauri Dec 31 '12 at 9:29
  • my god. That is an awesome answer. Thank you. I tried to upvote it a second time. I am learning something by reading your wonderful explanation. – Cris Stringfellow Dec 31 '12 at 9:42
  • 2
    You could upvote my comments too ;-) – F. Hauri Dec 31 '12 at 9:46
  • @CrisStringfellow Answer edited: by the use of -p switch to perl command, the script was lightened! – F. Hauri Mar 31 '16 at 5:53
10

just use awk with sleep:

awk '{print $0; system("sleep .1");}' log.txt
  • This worked for me and for my situation was the best option rather than the script options above. Unsure why this answer is down-voted. – Citizen Kepler Jan 17 '18 at 19:09
  • 2
    Unlike the perl solution it is quite readable. – Gunslinger Sep 5 '18 at 7:26
  • 1
    @Gunslinger: Syntax system(*sleep .1") will generate 10 forks / secs! This could be written perl -pe 'system "sleep .1"' log.txt: Readable too, but very expensive (not system friendly!) – F. Hauri Feb 12 at 19:13
  • I too prefer this readable answer. The only thing is it will fire the shell sleep command for every line it outputs. But being a perfect readable one liner I don't care. – itsafire Jun 19 at 10:46
0

I'm late to the party, but I found this would be a useful learning exercise to try in python, so I'll put up what i got:

#!/usr/bin/env python3

import argparse
from time import sleep

parser = argparse.ArgumentParser(description='Echo a file slowly')
parser.add_argument('-i',
                    '--input-file',
                    type=argparse.FileType('r'),
                    default='-')
parser.add_argument('-d',
                    '--delay-in-ms',
                    type=int,
                    default='100')
args = parser.parse_args()

for line in args.input_file:
    print(line.rstrip())
    sleep(args.delay_in_ms/1000.0)

It accepts input from stdin or as an argument (-i) and by default writes one line per 1/10th of a second, but that can be changed with another argument (-d).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.