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

I am trying to monitor a capture directory for new files. Then copy only these new files to another directory 2. I currently have this bit of code which works fantastic other than I need it to ignore files that have already been copied from directory 1. Files on directory 2 will be immediately processed and deleted.

#!/usr/bin/perl -w use strict; use File::Copy; use File::stat; use POSIX qw(strftime); my $dirname = "R:"; my $dirname2 = "M:"; my $filterstring = "\\.mpg\$"; my $sleep = 5; # how long to sleep for while (1) { opendir ( DIR, $dirname ) || die "Error in opening dir $dirname\n" +; foreach my $filename (readdir(DIR)) { next if not $filename =~ m/$filterstring/; my $old_file = $dirname . $filename; my $new_file = $dirname2 . $filename; print "Copying $old_file to $new_file\n"; copy($old_file, $new_file); } closedir(DIR); sleep $sleep; 1; }
  • Comment on Monitor directory for new files and copy only new files to another directory
  • Download Code

Replies are listed 'Best First'.
Re: Monitor directory for new files and copy only new files to another directory
by McA (Priest) on Oct 09, 2014 at 19:34 UTC

    Hi,

    if you are able to move the already copied files into an archiv directory, I would do the following:

    Rename the found file to a filename which will be never hit by incoming files (some tilde before and after, or somthing like that). This would serve as flag that the file is in progress.

    Copy this file into the target directory. This may last some time.

    After successful copy move the source file into the archive directory rolling back the initial filename change.

    Rename the target file in the target directory back to the initial filename.

    When you log every step you should be able to find out weired circumstances when this process terminates abnormally. And you can be sure that the file in the target directory will only be fetched when completely written. With the code above you can't guarantee that.

    When you want you can also delete the source file in the source archiv.

    Best regards
    McA

Re: Monitor directory for new files and copy only new files to another directory
by Discipulus (Canon) on Oct 10, 2014 at 07:04 UTC
    I see you are under winz OS. I found an ancient (13 years old ?) program by Jenda that do something very similar.

    I would post the link if i have found it at monastery, but seem i connot find it. So i post directly the code.

    I hope Jenda will not be hurted by this.

    ### original code by Jenda as found at http://www.perlmonks.org/?node_ +id=105128 #perl -w # watch_dirs.pl use strict; #use G; use File::Copy; use Win32::IPC qw(wait_any); use Win32::ChangeNotify; use Win32::AbsPath qw(Relative2Absolute); use Win32::FileOp qw(:DIALOGS); use vars qw($VERSION $path @paths $i @notify @data $file $pathnum); $VERSION = "1.0"; if (@ARGV) { @paths = @ARGV; Relative2Absolute @paths; foreach (@paths) { die "Ussage: watch_dirs.pl [directory ...]\n\t\tversion $VERSI +ON by Jenda.Krynicky.cz (c) 2001\n" unless -d $_; } } else { while (1) { $path = BrowseForFolder "Watch directory for changes\nClick [C +ancel] to finish directory selection" , CSIDL_DRIVES, BIF_RETURNONLYF +SDIRS or last; push @paths, $path; } die "OK. No directory selected. I'm quiting.\n" unless @paths; } print "Watching directories:\n"; for ($i=0;$i<=$#paths;$i++) { $path = $paths[$i]; print "\t$path\n"; mkdir $path.'\\_b_a_c_k_u_p', 0777; opendir DIR, $path; while ($file = readdir DIR) { next if -d $path.'\\'.$file; $data[$i]->{$file} = (stat($path.'\\'.$file))[9]; } closedir DIR; push @notify, Win32::ChangeNotify->new($path,0,"LAST_WRITE SIZE FI +LE_NAME"); }; print "\n"; while (1) { $pathnum = wait_any(@notify) or die "Can't monitor the directory: +$!\n"; $pathnum--; # arrays always start from zero not one my $pathdata = $data[$pathnum]; my $path = $paths[$pathnum]; opendir DIR, $path; while ($file = readdir DIR) { next if -d "$path\\$file"; my $time = (stat("$path\\$file"))[9]; if ($pathdata->{$file} != $time) { print "$path\\$file has changed!\n"; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);$mon++;$year += 1900; my $timestr = sprintf '%04d%02d%02d-%02d%02d%02d',$year,$m +on,$mday,$hour,$min,$sec; copy "$path\\$file" => "$path\\_b_a_c_k_u_p\\$file.$timest +r"; $pathdata->{$file} = $time; } } closedir DIR; $notify[$pathnum]->reset; } =pod PerlApp perlapp watch_dirs.pl -f -c -x "-info=fileversion=1.0;productversion=1 +.0;internalname=watch_dirs.pl 1.0;originalfilename=watch_dirs.exe;com +panyname=Jenda Krynicky;productname=watch_dirs;legaltrademarks=Jenda. +Krynicky.cz;filedescription=Watch dirs for changes and make backup;le +galcopyright=Jenda.Krynicky.cz" =cut
    HtH
    L*

    UPDATE:i was seraching in the wrong place: the program is at his homesite!

    L*
    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Monitor directory for new files and copy only new files to another directory
by GotToBTru (Prior) on Oct 09, 2014 at 19:44 UTC

    Append another extension to the files in directory 1 when you copy them. Your next .. $filterstring will filter them out in subsequent runs.

    1 Peter 4:10
      This is actually a good idea and works. The only concern is that "directory 1" is an starting point for another larger workflow. So extensions and naming convention must remain original.
Re: Monitor directory for new files and copy only new files to another directory
by james28909 (Deacon) on Oct 10, 2014 at 08:30 UTC
    "I currently have this bit of code which works fantastic other than I need it to ignore files that have already been copied from directory"

    here is what i came up with:
    use strict; use warnings; use diagnostics; use File::Copy::Recursive qw(dircopy rcopy); use File::Path qw(make_path); my $dirname = "$ENV{USERPROFILE}\\Dropped Box\\"; my $dirname2 = "C:\\Dropped Stuff\\"; my $sleep = 5; # how long to sleep for while (1) { opendir( DIR, $dirname ) || die "Error in opening dir $dirname\n"; foreach my $filename ( readdir(DIR) ) { if ( not -e $dirname2 . $filename ) { print "copying $dirname$filename to $dirname2$filename\n"; dircopy( $dirname . $filename, $dirname2 . $filename ); rcopy( $dirname . $filename, $dirname2 . $filename ); } else { print "files already exists so im skipping\n"; } } closedir(DIR); sleep $sleep; 1; }
    this will check for if files exists and if it doesnt exists in destination, then copy. then it will also update files int he root copy to directory if they have changes, but not directories and sub directorie)
    if you dont want to copy a file that has been copied, it would barely be trivial to setup a small condition to check a listfile that gets populated with filenames that have been copied. and add a check to see if the filename exists in the listfile, and if it does skip it :)

    oh btw, this will copy any file that gets dropped in the directory. and does NOT do a backup, but that could be easily integrated ;)
      i wonder how someone could set this up for directory recursion that would include all folders and subfolders?
Re: Monitor directory for new files and copy only new files to another directory
by james28909 (Deacon) on Oct 10, 2014 at 06:27 UTC
    hey this is an awesome idea :)
    but, why are you only interested in just .mpg? why dont you copy any other file type that gets dropped into this directory? something like this would work great if you could setup a server to copy or upload the files to automatically.

      This is a local RAID drive on a Mpeg capture system. We do archive the files on network storage but that is manual process done by staff at their convenience. I am trying to monitor the capture folder for new captures and then just copy the file to a QC workflow watch folder.I have a software called "Second Copy" doing this now but it sort of does the same thing my Perl script does. Except it copies the entire list of mpg files every time there is a file/s change. So I am just trying to get a simple script that will copy only newly captured files to a network folder.