http://www.perlmonks.org?node_id=872039

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

Hi Monks;

The following code works well for single file, what i want it to do is monitor the mtime of a file, if it has been modified, copy it to a new directory immediately as an automatic backup procedure.

use strict; use warnings; use File::Monitor; use File::Monitor::Object; use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove); my $wFile="1.doc"; my $wFileMonitor = File::Monitor->new(); $wFileMonitor->watch($wFile); $wFileMonitor->scan; while (1){ my @changes = $wFileMonitor->scan; foreach my $object (@changes) { my $modified = $object->mtime; print "$wFile changed\n"; fcopy ("$wFile","c:\\newpath"); # automatic backup if the file h +as been modified } }

But i have many files for which to be monitored, i want to give it a file list or array like the following codes:

use strict; use warnings; use File::Monitor; use File::Monitor::Object; use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove); my @Files=("1.doc","2.doc","3.xls"); my $wFileMonitor = File::Monitor->new(); foreach my $wFile (@Files){ $wFileMonitor->watch($wFile); #-- First scan does nothing $wFileMonitor->scan; while (1){ my @changes = $wFileMonitor->scan; foreach my $object (@changes) { my $modified = $object->mtime; print "$wFile changed\n"; fcopy ("$wFile","c:\\newpath"); } } }

What the codes output is just monitor the first file in the file array or list, when i did some modifies to the other file in the list there was no monitor response. But i want they were monitored simultaneously. Can you give me some advices! Any help will be appreciated!

Replies are listed 'Best First'.
Re: File::Monitor problem with batch files
by tokpela (Chaplain) on Nov 17, 2010 at 21:20 UTC

    There are a couple of options.

    One option is to create a thread for each file you want to monitor. If you go this route, check out the Thread::Queue module. You should run a search on this site for many good starting points. BrowserUK has given lots of advise and examples.

    A second option is to monitor a directory and then make a copy if you receive an modification event. There is a module Win32::ReadDirectoryChangesW which will tell you which file was modified and allow you to respond. It is a wrapper around the Win32 API ReadDirectoryChangesW. It is not on CPAN but can be found on this site - so you will need to copy and paste the module into your personal library directory.

    An short example, pieced from how I have used this module (untested).

    use strict; use warnings; use Win32::ReadDirectoryChangesW; my $path = 'c:/directory'; my $subtree = 1; my $filter = FILE_ACTION_ADDED | FILE_ACTION_MODIFIED; my $rdc = Win32::ReadDirectoryChangesW->new(path => $path, subtree => 1, filter => $filter); my @results = $rdc->read_changes; while (scalar @results) { my ($action, $filename) = splice(@results, 0, 2); if ($action == FILE_ACTION_ADDED || $action == FILE_ACTION_MODIFIED +) { # perform your backup here } }

    There is also a third option if you have more than one directory which would be to use both methods 1 and 2.

      Thank you very much! I have setup the win32::readdirectorychangesw module on my hard drive by copying the pm file to the directory of perl. i tried the following code as you told me:

      use strict; use warnings; use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove); use Win32::ReadDirectoryChangesW; my $path = 'c:\\PAPER'; my $subtree = 1; my $filter = FILE_ACTION_ADDED | FILE_ACTION_MODIFIED; my $rdc = Win32::ReadDirectoryChangesW->new(path => $path, subtree => 1, filter => $filter); my @results = $rdc->read_changes; while (scalar @results) { my ($action, $filename) = splice(@results, 0, 2); if ($action == FILE_ACTION_ADDED || $action == FILE_ACTION_MODIFIED +) { # perform your backup here fcopy ("$filename","p:\\phdpaper"); } }

      But i got a wrong message: Bareword "FILE_ACTION_ADDED" not allowed while "strict subs" in use at C:\PAPER\1.pl line 9. Bareword "FILE_ACTION_MODIFIED" not allowed while "strict subs" in use at C:\PAPER\1.pl line 9. Bareword "FILE_ACTION_ADDED" not allowed while "strict subs" in use at C:\PAPER\1.pl line 21. Bareword "FILE_ACTION_MODIFIED" not allowed while "strict subs" in use at C:\PAPER\1.pl line 21. Execution of C:\PAPER\1.pl aborted due to compilation errors.

      Then i delete the "use strict" script, runing...., but still got the following error: Can't locate object method "new" via package "Win32::ReadDirectoryChangesW" (perhaps you forgot to load "Win32::ReadDirectoryChangesW"?) at C:\PAPER\1.pl line 11.

      So, can you help me figure out what the problem was? Thanks a lot!

        Actually, I looked at the module and the package name is

        package Win32::ReadDirectoryChanges;

        The bareword errors were probably because of the incorrect package name. Change the name of the module you copied and pasted to Win32::ReadDirectoryChanges (without the W).

        Try to change the code to:

        use strict; use warnings; use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove); use Win32::ReadDirectoryChanges; my $path = 'c:\\PAPER'; my $subtree = 1; my $filter = FILE_ACTION_ADDED | FILE_ACTION_MODIFIED; my $rdc = Win32::ReadDirectoryChanges->new(path => $path, subtree => 1, filter => $filter); my @results = $rdc->read_changes; while (scalar @results) { my ($action, $filename) = splice(@results, 0, 2); if ($action == FILE_ACTION_ADDED || $action == FILE_ACTION_MODIFIED +) { # perform your backup here fcopy ("$filename","p:\\phdpaper"); } }

Re: File::Monitor problem with batch files
by Illuminatus (Curate) on Nov 17, 2010 at 21:59 UTC
    Have you looked at File::Rsync? It does not require iterative logic to process individual files, and will work either intra- or inter-machine.

    fnord

      You know, I am a newbie for perl programming and really did not look the file::rsync module, maybe i should learn it later.

      I have solved the problem by file::monitor::delta as directed by tokpela (Friar) and Khen1950fx (Prior), thanks to all you guys' help!

Re: File::Monitor problem with batch files
by Khen1950fx (Canon) on Nov 17, 2010 at 23:37 UTC
    I tried it like this:
    #!/usr/bin/perl use strict; use warnings; use File::Monitor; my @Files = qw(1.doc 2.doc 3.xls); my $wFileMonitor = File::Monitor->new(); foreach my $wFile(@Files){ $wFileMonitor->watch($wFile); } $wFileMonitor->scan; my @changes = $wFileMonitor->scan; foreach my $change(@changes) { if ($change->is_mtime) { my $mtime = $change->is_mtine; my $old_mtime = $change->old_mtime; my $new_mtime = $change->new_mtime; print "$mtime has changed mtime from $old_mtime" . "to $new_mtime\n"; } }

      Your code just stop immediately after running, I think it need at least one loop as while (1){ ..... } or something, but after i added the while loop, it still not work for me, it looks like the code i have posted. Thanks anyway!

        It actually was working just as it should work. I called scan twice, so it shut down. I don't think that you need a while loop. For example, you can turn on the monitor, and it will dutifully observe what it's supposed to watch:
        #!/usr/bin/perl use strict; use warnings; use File::Monitor; my @Files = qw(1.doc 2.doc 3.xls); my $wFileMonitor = File::Monitor->new(); foreach my $wFile(@Files){ $wFileMonitor->watch($wFile); } $wFileMonitor->scan;
        Now the monitor is on. It will stay on until you call the second scan---then it reports the changes and shuts off.
Re: File::Monitor problem with batch files
by xbmy (Friar) on Nov 18, 2010 at 22:46 UTC

    Finally, I tried the following code, it works perfectly for monitoring all the files in a directory! Thanks you all!

    use File::Monitor; use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove); my $monitor = File::Monitor->new(); $files= 'c:\\PAPERS'; opendir(AA,$files) || return; my @list = readdir(AA); closedir (AA); foreach $file (@list) { $monitor->watch( $file ); } $monitor->callback( mtime => sub { my ($file, $event, $change) = @_; print "$file has been modified \n\n"; fcopy ("$file","p:\\phdpaper"); } ); while (1) { $monitor->scan; sleep 10; # After the first scan we get a list of File::Monitor::Delta obje +cts # that describe any changes my @changes = $monitor->scan; for my $change (@changes) { # Call methods on File::Monitor::Delta to discover what change +d if ($change->is_mtime) { my $name = $file_name; } } }