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

Hi Monks,

I am an old dog trying to learn some PERL tricks. I am getting back into PERL after more than a decade away. I am attempting to use File::Find to search for multiple patters and return the results as an array.

I have read the Beginners Guide and combed this site but I must be missing something simple?

Here's what I have so far

use strict; use warnings; use Posix; use File::Find; sub findfile { my @Result = (); my $Start = $_[0]; print "Start: [$Start]\n"; if (scalar(@_) >= 2) { foreach my $x (1 .. scalar(@_) - 1) { find( sub { my @finds = grep { $_ =~ /$_[$x]/ } ($File::Fi +nd::name); if (scalar(@finds) > 0) { my $find=$finds[0]; if ($find eq $File::Find::name) { push @Result, $File::Find::name; return; } } }, $Start ); } return @Result; } } my @Paths = findfile("C:/Program Files (x86)/Nimsoft/bin", "*.exe", "* +.txt"); foreach ( @Paths ) { print "$_\n"; }
The output is as follows:
C:/Program Files (x86)/Nimsoft/bin C:/Program Files (x86)/Nimsoft/bin/nimalarm.exe C:/Program Files (x86)/Nimsoft/bin/nimboss.exe C:/Program Files (x86)/Nimsoft/bin/nimbus.exe C:/Program Files (x86)/Nimsoft/bin/nimqos.exe C:/Program Files (x86)/Nimsoft/bin/nimqosdefinition.exe C:/Program Files (x86)/Nimsoft/bin/pu.exe C:/Program Files (x86)/Nimsoft/bin C:/Program Files (x86)/Nimsoft/bin/nimalarm.exe C:/Program Files (x86)/Nimsoft/bin/nimboss.exe C:/Program Files (x86)/Nimsoft/bin/nimbus.exe C:/Program Files (x86)/Nimsoft/bin/nimqos.exe C:/Program Files (x86)/Nimsoft/bin/nimqosdefinition.exe C:/Program Files (x86)/Nimsoft/bin/pu.exe

I am going cross-eyed trying to figure this out. Why am I getting the list of exe files twice? Also, I don't get why the directory itself is printing out?

Any assistance would be very much appreciated

I also know I am showing Windows here. But, if possib;e the routine would be able to run on Linux/Unix as well.

Replies are listed 'Best First'.
Re: Finding files with multiple patterns
by davido (Cardinal) on May 14, 2013 at 23:45 UTC

    I started to walk through the logic, and explain why it's not working as you would like, but there were more than a couple smoking guns, and finally I decided to show you one way I might do it, if I were to use File::Find for this.

    I will mention however, that your use of grep is incorrect (you should be passing a useful list to it). You would also avoid some confusion by unpacking your function's args list at the top, as $_[0] within your anonymous sub is not referring to the parent sub's arg list. Also, unless you test whether the thing that File::Find is looking at is a file, you will see directories too. Furthermore, "*.exe", used as a regexp pattern isn't doing what you want.

    The following has been tested on Linux, with an appropriate change to the target starting path. ;)

    use File::Find; my @files_found = find_files( 'C:/Program Files (x86)/Nimsoft/bin', [ qr/\.exe$/, qr/\.txt$/ ] ); print "$_\n" for @files_found; sub find_files { my( $start, $patterns_aref ) = @_; my @found; find( sub { if( -e && -f ) { for my $pattern ( @$patterns_aref ) { if( m/$pattern/ ) { push @found, $File::Find::name; last; # Matched, so we don't need to test other rules. } } } }, $start ); return @found; }

    It should work on Windows as well.

    Update: I mentioned at the outset that this is one way I might do it if I were to use File::Find. I said that because I would probably prefer using the cleaner feeling File::Find::Rule instead; although I haven't worked through the solution, I suspect it would turn out easier to read.


    Dave

      Thanks for the reply. I appreciate the help. Apparently I am more rusty than I thought :).

        Keep at it! :) You're on your way.


        Dave