1#!/usr/bin/perl -w 2 3use IO::KQueue; 4use Getopt::Std; 5 6my $START = 10; 7 8my %opts; 9 10getopts('h', \%opts); 11help() if $opts{h}; 12 13my $file = shift(@ARGV) || "-"; 14open(my $fh, $file) || die "$0: open($file): $!"; 15 16my @buf; 17while (<$fh>) { 18 $buf[$. % $START] = $_; 19} 20my @tail = (@buf[ ($. % $START + 1) .. $#buf ], 21 @buf[ 0 .. $. % $START ]); 22for (@tail) { 23 print if $_; # @tail may begin or end with undef 24} 25 26my $kq = IO::KQueue->new(); 27 28$kq->EV_SET(fileno($fh), EVFILT_READ, EV_ADD, 0, 0, \&read_file); 29$kq->EV_SET(fileno($fh), EVFILT_VNODE, EV_ADD, NOTE_DELETE | NOTE_RENAME, 0, \&reopen_file); 30 31while (1) { 32 my @events = $kq->kevent(); 33 34 foreach my $kevent (@events) { 35 my $sub = $kevent->[KQ_UDATA]; 36 $sub->($kevent) if ref($sub) eq 'CODE'; 37 } 38} 39 40sub read_file { 41 my $kev = shift; 42 43 if ($kev->[KQ_DATA] < 0) { 44 print "$file has shrunk\n"; 45 seek($fh, 0, 2); 46 return; 47 } 48 49 while (<$fh>) { 50 print; 51 } 52} 53 54sub reopen_file { 55 # renamed or deleted... 56 close($fh); 57 open($fh, $file) || die "$0: $file went away\n"; 58 $kq->EV_SET(fileno($fh), EVFILT_READ, EV_ADD, 0, 0, \&read_file); 59 $kq->EV_SET(fileno($fh), EVFILT_VNODE, EV_ADD, NOTE_DELETE | NOTE_RENAME, 0, \&reopen_file); 60} 61 62sub help { 63 print <<EOT; 64$0 [-h] [file] 65 66Tail a file forever, like tail -F. 67EOT 68 exit(0); 69} 70