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

Fellow monks, I am trying to understand how this subroutine works and I just cannot workout the logic. Also, "findfiles" is not part of a module as far as I know and it keeps erroring out when I run the program. It is also not a defined variable, either. Error at bottom.
use strict; use warnings; use diagnostics; use autodie; use File::Copy::Recursive qw( rcopy ); use File::Path qw( mkpath remove_tree ); use File::Glob qw( :globally ); use File::Find; use File::Spec; use Getopt::Long; use Pod::Usage; use POSIX qw(strftime); use File::Spec::Functions; sub DirCompare{ my ( %old_list, %new_list, @diffs); for my $file (findfiles($ARCHIVE) ) { $old_list{$file} = 1; } for my $file (findfiles($SourceDir) ) { $new_list{$file} = 1; } for my $file (sort keys %old_list) { if ( !defined $new_list{$file} ) { push @diffs, "Old file not in new: $file"; } } for my $file (sort keys %new_list) { if ( !defined $old_list{$file} ) { push @diffs, "Old file not in new: $file"; } } if (@diffs) { my $msg = sprintf "WARNING: %s\n", join "\nWARNING: ", @errors; check_ok( $msg ); } return; }

Undefined subroutine &main::findfiles called at deploy.pl line 154 (#1) (F) The subroutine indicated hasn't been defined, or if it was, it has since been undefined. Uncaught exception from user code: Undefined subroutine &main::findfiles called at deploy.pl line 154. at deploy.pl line 154 main::DirCompare() called at deploy.pl line 243

If you have any input as to how I can better form the subroutine, please share your ideas.

Replies are listed 'Best First'.
Re: Comparing files in directory
by Athanasius (Archbishop) on Dec 11, 2014 at 16:25 UTC

    Hello smturner1,

    I am trying to understand how this subroutine works and I just cannot workout the logic.

    Despite the various missing pieces ($ARCHIVE, $SourceDir, @errors, ...), the general logic of the subroutine is fairly straightforward. The aim is to populate an array, @diffs, with details of the differences between the two directories $ARCHIVE and $SourceDir. First, the contents of $ARCHIVE are stored as keys in the hash %old_list, and the contents of $SourceDir are stored as keys in the hash %new_list. With this information available, it is then easy to determine which files are present in $ARCHIVE but not in $SourceDir:

    for my $file (sort keys %old_list) { if ( !defined $new_list{$file} ) { push @diffs, "Old file not in new: $file"; } }

    This tests each filename in %old_list and looks for it in %new_list. And this code reveals the reason for storing filenames as hash keys rather than as array elements: it is much simpler to test whether a key is present in a hash using defined than to search through an array. Incidentally, the call to sort is pointless here, and exists is usually preferable to defined in this situation.

    In a similar manner, the next for loop identifies those files which are present in $SourceDir but absent from $ARCHIVE.

    The specifically “Perlish” aspect to this code is the elegant use of hashes to search through lists. See, for example, How can I tell whether a certain element is contained in a list or array?

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Athanasius, your explanation was very helpful. I will tune-up the loop construct as you have suggested.

      As far as the custom sub goes (findfiles) there is not any files that go with it. I am the original developer of this code with the help of a Senior Developer. The section of code in question was his suggestion. He probably mentioned that I need to define findfiles, but those directions were not in my notes, nor my recollection. He is on vacation now.

      I was researching File::Find and came up with this piece of code. It is a little cryptic and I feel it is missing something. Monks, can you please take a look and help me with the details? I noted the new code with "New piece of code".

      use strict; use warnings; use diagnostics; use autodie; use File::Copy::Recursive qw( rcopy ); use File::Path qw( mkpath remove_tree ); use File::Glob qw( :globally ); use File::Find; use File::Spec; use Getopt::Long; use Pod::Usage; use POSIX qw(strftime); use File::Spec::Functions; #Set command line arguments my ($website, $old_ip, $new_ip) = @ARGV; #Set vars my $TIMESTAMP = strftime("%Y%m%d%H%M", localtime); + #my $volume = 'C:/'; + my $SourceDir = File::Spec->catfile((qw(C: Users st2641 Desktop S +ource_dir))); my $destinationDir = File::Spec->catfile(qw(C: Users st2641 Desktop), +$website); my $ARCHIVE = File::Spec->catfile(qw(C: Users st2641 Desktop), +join('_', $website, $old_ip, $TIMESTAMP)); my @WEBSITES = qw( three five calnet-test ); my $file; my @errors; my @file_list; sub DirCompare{ my ( %old_list, %new_list, @diffs); ###for my $file ($ARCHIVE) { ###$old_list{$file} = 1; ###} #////New piece of code for my $file ( find ( sub { $new_list { $file } = 1 }, $SourceDir ) +); for my $file (keys %old_list) { if ( !exists $new_list{$file} ) { push @diffs, "Old file not in new: $file"; } } for my $file (sort keys %new_list) { if ( !defined $old_list{$file} ) { push @diffs, "Old file not in new: $file"; } } if (@diffs) { my $msg = sprintf "WARNING: %s\n", join "\nWARNING: ", @errors; check_ok( $msg ); } return; } $file (find( sub {$new_list{$file} = 1}, $SourceDir ) );

        Providing there are no sub-folders, the essentials are this

        #!perl use strict; use File::Find; my $old = 'c:/....'; # as reqd my $new = 'c:/....'; # as reqd my %old_list =(); my %new_list =(); find( sub { $old_list{$_} = 1}, $old); find( sub { $new_list{$_} = 1}, $new); my $diffs = DirCompare(); print "$_\n" for @$diffs; sub DirCompare { my @diffs = (); for my $file (sort keys %old_list) { if ( !defined $new_list{$file} ) { push @diffs, "Old file not in new: $file"; } } for my $file (sort keys %new_list) { if ( !defined $old_list{$file} ) { push @diffs, "New file not in old: $file"; } } return \@diffs; }
        poj
Re: Comparing files in directory
by toolic (Bishop) on Dec 11, 2014 at 15:55 UTC
    "findfiles" is not part of a module as far as I know
    You might be able to use exports -- which module exports are used? to locate subs which you should import.

    Perl is case-sensitive; check your spelling and case.

Re: Comparing files in directory
by Anonymous Monk on Dec 11, 2014 at 15:52 UTC

    This is obviously an excerpt from a longer script. So I understand correctly that you inherited this script from somewhere and are trying to make it work?

    None of the several listed modules have a findfiles function, in fact with a quick search I can't seem to find any CPAN module that defines a function named that. That means the findfiles function must be custom and defined somewhere else in the script, or in a custom library that goes along with the script.

    You should search for the string "findfiles" in all of the files that are part of this script and see where it is defined. Either you've inherited an incomplete set of files, or the module that provides findfiles isn't getting loaded into the script for some reason. But if that's the case, we'll need to know more about the layout of the files and how they load each other to help you better.