morgon has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I would like to monitor changes to a file on Debian via inotify.

Here my attempt:

use strict; use Linux::Inotify2; $|++; my ($file_to_watch)=@ARGV; my $notify = Linux::Inotify2->new or die $!; my $w = $notify->watch ($file_to_watch, IN_MODIFY, \&on_change) or die + $!; sub on_change { my($e)=@_; print $e->fullname, " changed\n"; } while(1) { print "polling\n"; my $n = $notify->poll; print "handled $n events\n"; }
The strage behaviour is now that when I run this script on a file and make a change to the file, nothing happens.

When I then make another change, the event handler is triggered but from then on no more changes are reported...

So strange as it seems the above code on my system only detects the second change made to a file - not quite what I am after...

What am I doing wrong here?

Many thanks!

Replies are listed 'Best First'.
Re: inotify problem
by Perlbotics (Archbishop) on Jun 05, 2012 at 21:33 UTC

    Same here (SuSE 11.4, Perl 5.14.1, Linux::Inotify2 V1.22) - works.

    As AM already said, it depends on how you change the file and if you rename the original file during the process. Try the following changes, run stat file_to_watch before and after your experiment.

    ## my $w = $notify->watch ($file_to_watch, IN_MODIFY, \&on_change) or + die $!; my $w = $notify->watch ($file_to_watch, IN_MODIFY | IN_MOVE_SELF, +\&on_change2) or die $!; ... sub on_change2 { my($e) = @_; print $e->fullname, " changed "; print " and also moved!" if $e->IN_MOVE_SELF; print "\n"; }

    E.g., if I mix edit the file using Emacs and echo, the program stops reporting changes. If I append to the Emacs backup file (file_to_watch~) the modifications are reported again. The original file is watched, regardless of the fact that it has been renamed.

    You could monitor the (file-name-)changes (IN_MOVE_SELF) and just re-init the watcher to keep track of further changes applied to the file identified by its name, not by its inode.

      Thanks for the suggestions, the problem is that when you get the event that the file has moved the copy does not yet exist (preventing a new watch), so you need to watch the directory as well...

      Here what seems to work for me, any improvements welcome:

      use strict; use Linux::Inotify2; use File::Basename qw(dirname); use File::Spec; $|++; my ($file_to_watch)=@ARGV; my $notify = Linux::Inotify2->new or die $!; &setup_watch; sub setup_watch { $notify->watch ($file_to_watch, IN_MODIFY | IN_MOVE_SELF, \&watch_fi +le) or die $!; } sub watch_file { my($e)=@_; my $fn = $e->fullname; if($e->IN_MODIFY) { print "$fn changed\n"; } if($e->IN_MOVE_SELF) { print "old $fn moved\n"; $e->w->cancel; my $dir = dirname $fn; $notify->watch ($dir, IN_CREATE, \&watch_dir) or die $!; } } sub watch_dir { my($e)=@_; my $fn = File::Spec->canonpath($e->fullname); return unless $fn eq $file_to_watch; $e->w->cancel; print "$fn newly created (probably changed)\n"; setup_watch; } while(1) { my $n = $notify->poll; print "handled $n events\n\n"; }
Re: inotify problem
by Anonymous Monk on Jun 05, 2012 at 20:52 UTC
    Your code works for me on my gentoo system. I used
    echo one >> foo echo two >> foo echo three >> foo
    to make three separate changes to the file. If you are using an editor. It may be writing to the changes to a temp file; removing the original file; then renaming the temp file to the original name.
      Thanks a lot for your point about the editor.

      In fact my code is working but I had been trying to track changes to a file made with vim.

      The problem was that I use a "backup"-option in my vimrc and what vim then seems to do is to move the original file to the backup-folder and continue to work on a copy. The copy has the same name as the watched file but of course a different inode-number, so subsequent changes are not detected by inotify.

      I wanted to use inotify to avoid polling but in the light of this I would also have to watch the directory to detect such things and the extra complexity is probably not worth it.

      Or is there another way to discover changes made to a file given by name (i.e. whose inode could change) apart from polling it?