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

Hi Monks!

I have to read this big directory within directories looking for files, I am trying to add the array that has the file names I am looking for into my search code as you can see. I am getting errors as

Global symbol "$fnames" requires explicit package

Is there a way to do this in the way I am doing?
Here is a sample code to show how I am trying.
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; ... my $res_files = list_dirs($dir,$datanames); sub list_dirs { my @locations = shift; my @fnames = shift; =code # This is after DUMP of the data in here. my @locations = [ '/var/www/allfiles' ]; my @fnames = [ { 'filename' => 'abc122.txt' }, { 'filename' => '333twom.txt' }, { 'filename' => '56test.txt' }, { 'filename' => 't445ok.txt' } ]; =cut my @files; find( { wanted => sub { push @files, $_ unless $_ eq '.' || $_ eq '..' +|| $_ !~ m/@$fnames->{filename}$/} , no_chdir => 1 }, @locations); print Dumper \@files; return \@files; }

Thanks for helping!

Replies are listed 'Best First'.
Re: Passing file names to search directories
by 1nickt (Canon) on Dec 28, 2016 at 20:00 UTC

    Global symbol "$fnames" requires explicit package

    This specific error is because you have an array named @fnames but you later try to dereference an undeclared arrayref named $fnames.

    You've also got an issue with the part of your syntax that tries to access the element within the array:

    @$fnames->{filename}
    That probably would not do what you think even if you didn't have the first problem. You can't access an element of an array, whether directly or via reference, by a key name.

    If, as seems possible from the code you posted, you are receiving into your sub listdirs() two arguments, each a reference to an array, you can get what I think you want with:

    my $res_files = list_dirs($dir,$datanames); sub list_dirs { my ( $locations, $fnames ) = @_; ... foreach my $href ( @{ $fnames } ) { my $filename = $href{'filename'}; # do something with filename ... } }
    Or you could get the list of filenames with something like:
    my $res_files = list_dirs($dir,$datanames); sub list_dirs { my ( $locations, $fnames ) = @_; my @filenames = map { $_->{'filename'} } @{ $fnames }; ... }
    But, this raises the question of why you have an array of anonymous hashes containing just one key-value pair?

    And in general, in Perl, there's usually a tool to do something common like what you want. You might like to use Path::Iterator::Rule:

    use Path::Iterator::Rule; ... my $res_files = list_dirs($dir,$datanames); sub list_dirs { my ( $locations, $fnames ) = @_; # assumes each is a simple arrayref my $finder = Path::Iterator::Rule->new; $finder->name( @{ $fnames } ); my $get_next_match = $finder->iter( @{ $locations } ); my @found_filenames; while ( defined ( my $match = $get_next_match->() ) ) { push @found_filenames, $match; } return \@found_filenames; }

    See also Yes, even you can use CPAN.

    Hope this helps!


    The way forward always starts with a minimal test.
      Great explanation, but how would I use this line or the array @filenames:
      my @filenames = map { $_->{'filename'} } @{ $fnames };

      in this line:
      find( { wanted => sub { push @files, $_ unless $_ eq '.' || $_ eq '..' + || $_ !~ m/@filenames$/} , no_chdir => 1 }, @dirs);

      That's where I am trying to understand the most, thanks!
        I found that this way, it works:
        my @filenames = map { $_->{'filename'} } @{ $fnames }; my $rx_fnames = join '|', @filenames; find( { wanted => sub { push @files, $_ unless $_ eq '.' || $_ eq '.. +' || $_ !~ m/$rx_fnames$/} , no_chdir => 1 }, @dirs);

        Any feed back is welcome!
Re: Passing file names to search directories
by FreeBeerReekingMonk (Deacon) on Dec 28, 2016 at 18:49 UTC
    Try something more like this:
    #!/usr/bin/perl use strict; use warnings; use 5.012; # http://perldoc.perl.org/File/Find.html use File::Find; # http://perldoc.perl.org/Getopt/Long.html use Getopt::Long; my @locations; my @fnames; my $verbose; my $ignorecase; GetOptions ( "location=s" => \@locations, "filename=s" => \@fnames, "ignorecase" => \$ignorecase, "verbose" => \$verbose) or die("Error in command line arguments @ARGV\n"); my $USAGE = "$0 -l <directory> -f <filename> [-v] example: $0 -l /var/www/allfiles -f abc122.txt -f 333twom.txt -f 56test.txt -f +t445ok.txt "; die $USAGE unless @fnames; die $USAGE unless @locations; my @files; find({ wanted => \&process, follow => 0, no_chdir => 1 }, @locations); sub process{ my $addfile = 0; for my $f (@fnames){ print "Considering '$_' in $f \n" if $verbose; if(index($_,$f)>-1){ ++$addfile; last; } } push @files, $File::Find::name if $addfile; } use Data::Dumper; print Dumper \@files;
Re: Passing file names to search directories
by LanX (Saint) on Dec 28, 2016 at 18:12 UTC
    This code has many issues!

    For a start, try renaming  my @fnames = shift; to  my $fnames = shift; (untested)

    you should inform yourself about the differences between plain @arrays and $array_references.

    And please remove the pod block, it's not a good approach to uncomment code.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      I am sorry, here is a better sample of what I meant:
      #!/usr/bin/perl use strict; use warnings; use File::Find qw(finddepth); use File::Find qw(find); ... my ($dir,$datanames); # These var(s) has values been passed to the sub my $res_files = list_dirs($dir,$datanames); sub list_dirs { my @locations = shift; my $fnames = shift; my @files; find( { wanted => sub { push @files, $_ unless $_ eq '.' || $_ eq '..' +|| $_ !~ m/@$fnames->[0]{filename}$/} , no_chdir => 1 }, @locations); print Dumper \@files; return \@files; } # This is after DUMP of the data. =code my @locations = [ '/var/www/allfiles' ]; my $fnames = [ { 'filename' => 'abc122.txt' }, { 'filename' => '333twom.txt' }, { 'filename' => '56test.txt' }, { 'filename' => 't445ok.txt' } ]; =cut