Cat file to terminal at particular speed of lines per second

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 perl

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

just use awk with sleep:

awk '{print $0; system("sleep .1");}' log.txt