In some code you might see an operator that looks like this <>. At first you might be confused what less-than greater-than mean together, or you might notice that it looks similar to the readline operator, just without the filehandle in it.

This operator is called the Diamond operator.

Diamond Operator

The Diamond operator is almost exclusively used in a while-loop. It allows us to iterate over the rows in all the files given on the command line.

So this script

examples/diamond_cat.pl

use strict;
use warnings;

while (my $line = <>) {
    print $line;
}

if we run it as

perl diamond_cat.pl FILE1 FILE2 FILE3

it will print the content of all 3 files line-by-line.

Just as the Unix cat command would do.

Diamond operator explained

When perl reaches the <> the first time it looks at the content of @ARGV that, by default, contains the values passed on the command line. (Though we can actually change the content of @ARGV while our script runs.)

It shifts the first element to the $ARGV scalar variable, opens the file that has its name in $ARGV and reads in the first line.

Subsequent encounters with <> will read in subsequent lines of the same file.

After the last line was read in from the current file, the next encounter with the diamond operator will repeat the above operation starting by shifting the first value from @ARGV to $ARGV.

If there are no more entries in the @ARGV array, the diamond operator will return undef and the while loop will exit.

If during the looping, one of the values in @ARGV is not an existing file, the opening of that file will fail. A warning will be printed to STDERR, the standard error channel, and the diamond operator will shift out the next element of the array.

As a special case, if at the time of the first encounter with <>, the @ARGV array is empty (because we have not supplied anything on the command line, or because we have already emptied it), then the diamond operator will fall back to act as <STDIN>, reading from the standard input.

Diamond operator - grep-ish

The following example is a simple version of the Unix/Linux grep command:

examples/diamond_grep.pl

use strict;
use warnings;

while (my $line = <>) {
    if ( $line =~ /perl/) {
        print $line;
    }
}

It iterates over the lines of the files given on the command line and prints out the ones that match our regex. In this case I used an explicit variable $line where the diamond assigned the current line, and I used the same variable in the regex and in the printing.

Diamond - using $_, the default variable

We can make the above example even more compact by remembering that certain operation in Perl will use $_, the default variable of Perl, if no explicit variable is given.

examples/diamond.pl

use strict;
use warnings;

while (<>) {
    print if /perl/;
}

In this example every time the Diamond Operator reads in a line, it assigns it to $_. Then the regex matching will apply to $_, and finally, if there was a match, the print function will print out the content of $_.

Filename and line counter

It is nice to be able to iterate over the lines of multiple files at once, but often, during the iterations we would like to know the name of the current file and the current line number. The next one is an almost good solution:

As mentioned earlier $ARGV contains the name of the file currently opened by the Diamond Operator. In addition there is a variable $. (aka. $INPUT_LINE_NUMBER or NR) that contains the current line number.

examples/diamond_cat_full.pl

use strict;
use warnings;

while (my $line = <>) {
    print "$ARGV $. $line";
}

It is an almost correct solution, but due to the way $. and the Diamond operator work, the $. won't be reset after a file was exhausted and thus the counter will not show the line number in the second file, but the total lines read so far. See the documentation of $. and the solution using eof.

examples/diamond_cat_full.txt

$ perl diamond_cat_full.pl  diamond_cat_full.pl diamond_cat.pl 
diamond_cat_full.pl 1 use strict;
diamond_cat_full.pl 2 use warnings;
diamond_cat_full.pl 3 
diamond_cat_full.pl 4 while (my $line = <>) {
diamond_cat_full.pl 5     print "$ARGV $. $line";
diamond_cat_full.pl 6 }
diamond_cat_full.pl 7 
diamond_cat.pl 8 use strict;
diamond_cat.pl 9 use warnings;
diamond_cat.pl 10 
diamond_cat.pl 11 while (my $line = <>) {
diamond_cat.pl 12     print $line;
diamond_cat.pl 13 }
diamond_cat.pl 14 

Filename an fixed line counter

examples/diamond_cat_full_close.pl

use strict;
use warnings;

while (my $line = <>) {
    print "$ARGV $. $line";
} continue {
    close ARGV if eof;
}


Resulting in:

examples/diamond_cat_full_close.txt

$ perl diamond_cat_full_close.pl  diamond_cat_full.pl diamond_cat.pl
diamond_cat_full.pl 1 use strict;
diamond_cat_full.pl 2 use warnings;
diamond_cat_full.pl 3 
diamond_cat_full.pl 4 while (my $line = <>) {
diamond_cat_full.pl 5     print "$ARGV $. $line";
diamond_cat_full.pl 6 }
diamond_cat_full.pl 7 
diamond_cat.pl 1 use strict;
diamond_cat.pl 2 use warnings;
diamond_cat.pl 3 
diamond_cat.pl 4 while (my $line = <>) {
diamond_cat.pl 5     print $line;
diamond_cat.pl 6 }
diamond_cat.pl 7