Cat file to terminal at particular speed of lines per second

catcommand linescrollingspeedunix

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.

Best Answer

Short and readable:

perl -pe "system 'sleep .003'" log.txt

I post this solutions because they are small and readable, as comments of DMas's answer seem promote this kind of solution!

But I hate this because: For this run, perl will fork to /bin/sleep 300x / seconds!

This is a big ressource consumer! Also a wrong good solutions!!

Using builtin sleep in

Unfortunely, builtin sleep is limited to integers. So we have to use select instead:

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))
Related Question