in reply to Opening a file at a designated offset based on closing of the file

I think a simple tell -> seek algorithm and by checking file size == 0 are not enough. Your log file might have been truncated, re-written to, and then the new file size grows bigger than before. If you seek to previous position, then the script will work, but now it has an undetected logic error. You will need to record the position + the last line you have seen, then you can make sure that is the line where you have last visited.

This will work if the log is not a rotating log. What if it is? Uh... my head hurts.

The following is my attempt during my lunch break - (Ok, I have deliberately chose not to use Pod::Usage)
use strict; use Getopt::Long; use IO::File; use Data::Dumper; GetOptions ( 'i|input=s' => \( my $INPUT = "./access.log" ), 'l|lastpos=s' => \( my $LASTPOS = "./lastpos.txt" ), 'f|feedback' => \( my $FEEDBACK = undef ), ); unless ( defined $INPUT && defined $LASTPOS ) { print <<EOF; Logfile Parser - Parse input log efficiently Usage: $0 [option] Options: -i|--input [filename] Specify the input log file name. -l|--lastpos [filename] Specify the name of last pos file. -f|--feedback Let the program print progress prompt. EOF exit(1); } # load the last pos information my $lastinfo; $lastinfo = ReadLastPosFile($LASTPOS) if -f $LASTPOS; print "Last position:\n", Dumper($lastinfo) if ($FEEDBACK); # verify the log file my $begin_pos = VerifyLastPosition($INPUT, $lastinfo); # process the log file my $f = new IO::File $INPUT, "r" or die "Could not open log file"; if ($begin_pos == -1) { die "Log file has not been changed since last run"; } else { seek $f, $begin_pos, 0; # seek to start of next line } my $next_pos = $begin_pos; my $next_line; while ($next_line = <$f>) { $begin_pos = $next_pos; $next_pos = tell; # process the log file here chomp($next_line); print "$next_line\n"; } # at here, begin pos is the position of the last line seek $f, $begin_pos, 0; chomp($next_line = <$f>); $lastinfo->{pos} = $begin_pos; $lastinfo->{text} = $next_line; print "Last Pos Info:\n", Dumper($lastinfo) if ($FEEDBACK); # ok, write the last info back to file WriteLastPosFile($LASTPOS, $lastinfo); exit(0); sub ReadLastPosFile { # last pos file format - <pos>|<last-line-seen> my $filename = shift; my $f = new IO::File $filename, "r" or die "Could not open lastpos file"; chomp(my $info = <$f>); my %lastinfo; ($lastinfo{pos}, $lastinfo{text}) = $info =~ /(\d+)\|(.*)/; return \%lastinfo; } sub WriteLastPosFile { my ($filename, $lastinfo) = @_; my $f = new IO::File $filename, "w" or die "Could not write to lastpos file"; printf $f "%s|%s\n", $lastinfo->{pos}, $lastinfo->{text}; } sub VerifyLastPosition { my ($logfile, $lastinfo) = @_; my $f = new IO::File $logfile, "r" or die "Could not open log file +"; seek $f, 0, 2; # seek to the end of the file my $eof = tell $f; return 0 if $lastinfo->{pos} >= $eof; # ok, file has been trimmed seek $f, $lastinfo->{pos}, 0; chomp(my $line = <$f>); # retrieve what we believe was the last li +ne return 0 if $line ne $lastinfo->{text}; # ok, file has been trimme +d my $begin_pos = tell $f; # otherwise start from next line # -1 means the file has not been changed since # last time it was parsed. return $eof == $begin_pos ? -1 : $begin_pos; }
  • Comment on Re: Opening a file at a designated offset based on closing of the file
  • Download Code