Question

I want to create a Perl (or Bash) script to create and plot data on-the-fly. That means I want to extract data from a log-file file.log, ideally don't write a tmp-file (if it's only possible with a tmp-file, this would be fine too) and plot with Gnuplot. As the logfile is growing, I want to see the additional information in the plot.

See this question for similar topic.

For Perl, what I have so far is this script:

#!/usr/bin/perl
use strict;
use warnings;

my $path = "file.log";   
my @grepped;
my $switch = "off";

open(INFILE,"< $path") or die $! \n";
while (my $line = <INFILE>) {

       if ($line =~ m{^Time = (\d+)}){
               push(@grepped,"$1\t");
       };

       if ($line =~ m{^Errors: local = (\d+), global = (\d+)}){
               push(@grepped,"$1\t");
               push(@grepped,"$2\n");
               $switch = "refresh";
       };


if ($switch eq "refresh"){

open(GP, "| gnuplot -persist") or die: $! \n";
print GP << "GNU_EOF";

plot "@grepped" u 2:1
pause 1; refresh; reread;

GNU_EOF

close(GP);
}


}
close(INFILE)};

My first problem is that the on-the-fly functionality for Gnuplot isn't working. When the file.log gets changed, the refreshing of the Gnuplot-graph isn't working. I don't know if the -persist is correct here or if I have to use replot option instead of refresh. I tried it but it's not working for me.

Second problem is to read the data into array @grepped and use it in Gnuplot in one script for a changing input file. One additional restriction is that (as you can see from the code) gnuplot only should refresh the plot if a complete new line in @grepped is written. Otherwise errors would occur for sure because of the wrong assignment of data.

When I try a simple script like:

#!/usr/bin/gnuplot -persist
plot "data.dat" u 2:1
pause 1; replot; reread;

or

#!/usr/bin/gnuplot
plot "data.dat" u 2:1
pause 1; refresh; reread;

the on-the-fly part works if I change data.dat by hand and save it.

Was it helpful?

Solution

Here are two ways for plotting data on-the-fly.

Looping with gnuplot

You must call plot over and over again, the data is preprocessed by an external script. The minimal gnuplot script filter.gp is:

while (1) {
    plot '< ./myscript.pl' using 2:1
    pause 1
}

To stop this, hit Ctrl+C.

The Perl script for the preprocessing may look like the following myscript.pl:

#!/usr/bin/perl
use strict;
use warnings;

my $path = "file.log";   
my @grepped;
my $t = 0;
open(INFILE,"< $path") or die "$! \n";

while (my $line = <INFILE>) {
    if ($line =~ m{^Time = (\d+)}){
        $t = $1;
    };

    if ($line =~ m{^Errors: local = (\d+), global = (\d+)}){
        print "$t\t$1\t$2\n";
    };
};
close(INFILE);

Just run it with gnuplot filter.gp.

To make it more configurable, one can change the script to use a variable which is passed to gnuplot via the command line:

while (1) {
    plot '< ./myscript.pl -f '.path using 2:1
    pause 1
}

or use reread for this:

plot '< ./myscript.pl -f '.path using 2:1
pause 1
reread

Call this script with gnuplot -e "path='file.log';" filtermod.gp.

That works, but would filters the complete file over and over again.

Piping from Perl to gnuplot

Here is a Perl script, which basically works for me, but its my first real Perl script, so there may be some nonideal parts. Feel free to comment on that.

#!/usr/bin/perl
use strict;
use warnings;

my $path = "file.log";   
my @grepped;
my $switch = "off";

open(my $gp, "| gnuplot -persist") or die "$! \n";
$gp->autoflush(0);

use File::Tail;
my $file = File::Tail->new(name=>$path, maxinterval=>1, tail=>-1);

while (defined(my $line= $file->read)) {
    if ($line =~ m{^Time = (\d+)}){
        push(@grepped,"$1\t");
    };

    if ($line =~ m{^Errors: local = (\d+), global = (\d+)}){
        push(@grepped,"$1\t");
        push(@grepped,"$2\n");
        $switch = "refresh";
    };

    if ($switch eq "refresh") {
        print $gp <<"GNU_EOF";
plot '-' using 2:1
@grepped
e
GNU_EOF
        $gp->flush;
        $switch = "off"; 
    };
    Time::HiRes::sleep(0.1);
};

What I found to be important here is

  1. Looping over a changing file.
  2. The arrangement of the autoflush and flush.
  3. The sleep to allow gnuplot to process the data properly.

That worked for a very small test data file. Don't know if it will for a larger one as well, but should help you a bit further.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top