#!/usr/bin/perl -w # Strict use strict; use warnings; # Libraries use File::Basename; use FileHandle; # Main program $| = 1; # Create a closure 'psub', which will effectively "tail -f test.txt". my $psub = tail_closure("test.txt", 0); # Print the last 10 lines of the file my $ptext = $psub->(0); if ($ptext && @$ptext > 0) { my @last_10 = (@$ptext > 10)? splice(@$ptext, -10, 10): @$ptext; map { print "$_\n" } @last_10; } # Monitor any more text written to the file while (1) { my $ptext = $psub->(256); # Get any more text $ptext and map { print "$_\n" } @$ptext; # And print it select(undef, undef, undef, 0.05); # Short delay } # Subroutines # # Inputs: $1 ... a file to 'tail' (like "tail -f" in Unix) # $2 ... (optional) a flag; if zero, reads from the beginning # of the file; otherwise reads from the end # # Outputs: $1 ... a pointer to a subroutine to read successive # lines of text from the file. # # Parameters to the closure are: # # Inputs: $1 ... (optional) maximum number of lines # to read at a time (zero = unlimited) # # Outputs: $1 ... pointer to the lines read (zero on # error) # sub tail_closure { my ($fname, $b_from_eof) = @_; $b_from_eof ||= 0; my $fh = new FileHandle; open($fh, "<", $fname) or die "Failure to read file '$fname' ($!)\n"; $b_from_eof and seek $fh, 0, 2; require IO::Select; my $select = IO::Select->new(); $select->add($fh); my $text = ""; my $psub = sub { my ($max) = @_; $max ||= 0; my $count = 0; my $plines = [ ]; while (1) { my $c; # Next character my @ready = $select->can_read(0); # Ready to read from file? @ready or return $plines; # Not ready - return any lines my $nread = sysread $fh, $c, 1; # Read next character defined($nread) or return 0; # End of file ($nread > 0) or return $plines; # Done reading for now if ($c eq "\n") { # If newline ... push @$plines, $text; # Save line of text $text = ""; # and blank-out text # Return the lines when we reach the max ($max > 0 && ++$count == $max) and return $plines; } else { $text .= $c; # Add character to text } } }; return $psub; }