Command line counter with plain text file database

Gabor Szabo - Jan 29 '23 - - Dev Community

As part of the big counter example project, this example runs on the command line and uses a plain text file as back-end database.
It is probably the most basic version of all the counter examples, that provides a single counter.

Front-end

The front end is the command line. We just run the script as perl counter.pl.

Back-end

In this example the "database" is going to be a simple file called 'counter.txt' with only a number in it. The most recent value of the counter.

Code

use strict;
use warnings;

my $file = 'counter.txt';
my $count = 0;

if (-e $file) {
    open my $fh, '<', $file or die "Could not open '$file' for reading. $!";
    $count = <$fh>;
    close $fh;
}

$count++;
print "$count\n";

open my $fh, '>', $file or die "Could not open '$file' for writing. $!";
print $fh $count;
close $fh;


Enter fullscreen mode Exit fullscreen mode

After the initial statements to enable strict and warnings, we have declared a variable called $file that holds the name of the 'database' file we are going to use to store the latest value of the counter. This could be declared as a constant or read-only variable, but I did not bother.

During the execution of the script we will hold the value of the counter in the variable we cunningly called $counter. We declare it up-front and assign a default value of 0 to it.

Next we should be reading in the previous value of the counter. However, before we run the script for the first file, the file holding the number does not exist. Hence we need to do two things.

  1. When we declare $counter using the my keyword we also initialize it to the default value of 0. This way, even if the counter.txt file does not exist yet we can pretend that the previous value 0.
  2. Then, before we attempt to open the counter.txt file for reading, we check if it already exists using the -e operator.

Because at the first run the file does not exist, let's skip this code now and let's go straight to the part after the closing curly brace (}).

$count++;
print "$count\n";

open my $fh, '>', $file or die "Could not open '$file' for writing. $!";
print $fh $count;
close $fh;
Enter fullscreen mode Exit fullscreen mode

Using ++ the auto-increment operator of Perl, we increment the value of $count (which started out as 0 during the first execution). Then we print it out to the screen.

In the 3 lines after that, we open the 'counter.txt' file for writing. In the first run this will create the file,
in subsequent runs this will clean up the file to have no content. Then we use the print statement to write the new content of $count into the now empty file.
Finally, just to be nice, we close the file handle using the close function.

On the second and later executions of the script, the 'counter.txt' file will already exist. Thus the if (-e $file) will return true, and we enter the block:

if (-e $file) {
    open my $fh, '<', $file or die "Could not open '$file' for reading. $!";
    $count = <$fh>;
    close $fh;
}

Enter fullscreen mode Exit fullscreen mode

In this block we try to open the file for reading using the open function and throw an exception by calling the die function if we still cannot open the file. This should happen only in the most extreme situation and therefore it is ok to take drastic steps. If the open was successful, $fh, the newly declared variable will have the file handle in it. We can then use the <$fh> operator to read one line (the only one it has) from the file and finally we close the file.

No need to initialize the counter

Actually when we declared the $counter variable it was not really necessary to also assign 0 to it. It looks better for people coming from other programming languages, but in Perl even if we left it undef the script would work perfectly well.

That's because when using the ++ auto increment operator on a variable that has undef in it, that undef will act as if it was actually 0. It is probably the simplest form of autovivification in Perl. So we could have written

my $counter;
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .