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

hi great ppl !!

A simple one for you to shoot ...

I would like to get the filenames recursively from a Directory. For instance, I need to get all the *.pm files from one directory ( say, /home/Modules )..
This Modules dir has some sub-directory in it.. so i need to get all those filenames present in the sub-directory too..


use strict; use warnings; my $directory="/home/Modules"; opendir(DIR, $directory) or die "couldn't open $directory: $!\n"; my @files = grep { $_ ne '.' && $_ ne '..' } readdir DIR; open(FILE,">Modules"); print FILE join("\n",@files); close(FILE); closedir DIR;

The above code will fetch me all the files except . and ..

But my requirement is ;
1. I want to fetch only *.pm files which contains a method 'user_method' in it .. 2. I want to search all the sub-directories too.. 3. I require only those filenames..

How can i do that >>>


Thank You

  • Comment on Getting Filenames that contains a particular string recursively From a Directory
  • Download Code

Replies are listed 'Best First'.
Re: Getting Filenames that contains a particular string recursively From a Directory
by Corion (Patriarch) on Dec 06, 2005 at 10:26 UTC

    I guess that File::Find::Rule is what you want:

    use File::Find::Rule; my $directory = "/home/Modules"; my @files = File::Find::Rule ->name(qr/\.pm$/) ->grep('user_method') ->in($directory);

    Update: Fixed formatting error, spotted by frodo72

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Getting Filenames that contains a particular string recursively From a Directory
by BUU (Prior) on Dec 06, 2005 at 10:29 UTC
    use File::Find to recurse through subdirectories, open to open files and readline to read lines from the file. Assuming your code isn't particularly obfuscated, you can almost certainly use sub\s+user_method to match your subroutine.
    use File::Find; find( \&wanted, '.' ); sub wanted { open my $fh, $_ or die $!; local $/; my $file_contents = <$fh>; if( $file_contents =~ /sub\s+user_method/ ) { print "Matched $_!\n"; } }
    Note that I undefined $/ so that my readline reads the entire contents of the file in to one variable. This is because your method declaration might contain an embedded new line. This method is slightly less efficient than reading line by line, but for something of this nature it hardly matters, as perl modules are rarely more than a couple of hundred kilobyes.


    Update: Fixed my broken closing code tag.
Re: Getting Filenames that contains a particular string recursively From a Directory
by merlyn (Sage) on Dec 06, 2005 at 15:17 UTC
Re: Getting Filenames that contains a particular string recursively From a Directory
by vennirajan (Friar) on Dec 06, 2005 at 10:54 UTC
    hi Anonymous Monk,
          This code will help u.Getback to me if you have any comments.
    #!/usr/bin/perl -w use strict; open (FIND_CMD, "find /home/modules |"); chomp (my @findArr = <FIND_CMD>); close (FIND_CMD); my $pattern = '.pm$'; my $FilePattern = "sub user_method"; my $ResultFilePath = '/tmp/ModuleSearch'; foreach my $File ( @findArr ) { next if ( $File !~ m/$pattern/ ); system ("grep -iHl \"$FilePattern\" \"$File\" >> $ResultFilePath ") +; }

    Regards,
    S.Venni Rajan.
    "A Flair For Excellence"
                  -- BK Systems.

      There's nothing wrong in using an external find command in a pipe open, especially if you're not concerned about portability across systems.

      But then you're using perl to run what is fundamentally a shell script. In particular there's absoultely no need to fork out external grep's with that system.

      Also you have:

      open (FIND_CMD, "find /home/modules |"); chomp (my @findArr = <FIND_CMD>);
      I recommend:
      1. using the three args form of open and lexical filhandles,
      2. checking errors,
      3. avoiding to slurp in a potentially big list, whereas we have a syntactically sweet enough while loop just to process it one item at a time.
      my $pattern = '.pm$'; my $FilePattern = "sub user_method"; my $ResultFilePath = '/tmp/ModuleSearch'; foreach my $File ( @findArr ) { next if ( $File !~ m/$pattern/ );

      This will match Foo.apm; you may want to learn about \Q (and \E) in perldoc perlop.

      But since you're running an external find anyway, why not let it do the dirty job? (-name '*.pm')

      system ("grep -iHl \"$FilePattern\" \"$File\" >> $ResultFilePath ") +;

      I wish I had not seen that...

      All in all it may have been:

      #!/usr/bin/perl use strict; use warnings; open my $find, '-|', 'find /home/modules -name "*.pm"' or die "Can't run find(1): $!\n"; $\="\n"; while (<$find>) { chomp; open my $fh, '<', $_ or die "Can't open `$_': $!\n"; while (my $line=<$fh>) { print, last if $line =~ /sub user_method/; } } __END__

      A compressed version (having a huge pile o' magic in it) of which could be:

      #!/usr/bin/perl -ln use strict; use warnings; BEGIN { @ARGV='find /home/modules -name "*.pm"|' } local @ARGV=$_; /sub user_method/ and (print $ARGV), last while <> ; __END__

      (both untested)

        Hi !!

        Thanks for your valuable code !!

        I tried your first code.. the output is like this..

        /home/modules/one.pm /home/modules/two.pm /home/modules/three.pm
        I dono how to get only those filenames.. <br> i.e like one, two and three in an "array"<br>

        Thank u

      What happens if your program is run more than once?

      A reply falls below the community's threshold of quality. You may see it by logging in.