http://www.perlmonks.org?node_id=555084

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

I'm not a big fan of File::Find. To use it, I usually create a wanted function, load up an array with the stuff I want, and then process the array in my main block of code:
use File::Find; [...] my @fileList; find(\&wanted, qw($myDirectory)); sub wanted { if (-f $File::Find::name) { push(@fileList, "$File::Find::name) } } print "You've found " . join("\n\t", @fileList) . "\n";
To me, this just isn't very pretty because my @fileList array is being treated as being global to the &wanted funciton. Plus, it is slow because I have to wait for my array to fill up before I can use it.

The way around this is to put most of my file processing code with in the wanted function which also seems just as ugly since 90% of my script will end up inside wanted.

Maybe I really don't know the correct way to use File::Find. The Perl POD on this module concentrates more on find2perl than on using this module. However, it doesn't appear to be another method for File::Find.

Just how are you suppose to use File::Find? Most of the comments I've seen concentrate on automatically generating the "wanted" array. To me, this really isn't the problem. Heck, I'd be happy if File::Find simply walked the file tree, and I had to manually take the output and figure out if I wanted it or not. Something like this:

while ($file = File::Find::find(@directoryList)) { if (@wantThis) { print "Here's a file I want: $file\n"; } }
So, what is the best way to use Find::File?

Replies are listed 'Best First'.
Re: How do you make File::Find a bit less ugly?
by wfsp (Abbot) on Jun 13, 2006 at 16:28 UTC
    Hi qazwart

    I agree with you and I agree with davorg's suggestion to use File::Find::Rule. The rules can be a bit verbose so I put them in a sub.

    #!/usr/bin/perl use strict; use warnings; use File::Find::Rule; my $dir = 'c:/www/sw/category'; my $rule = get_rule($dir); while (my $file = $rule->match) { print "$file\n"; } sub get_rule{ my ($dir) = @_; my $rule = File::Find::Rule->new; $rule->or( $rule->new ->directory ->name(qr/^_/) ->prune ->discard, $rule->new ); $rule->name('*.html'); $rule->file->not( $rule->new->name('00template.html'), $rule->new->name('00greybox.html'), ); $rule->start($dir); return $rule; }
Re: How do you make File::Find a bit less ugly?
by davorg (Chancellor) on Jun 13, 2006 at 16:18 UTC
Re: How do you make File::Find a bit less ugly?
by merlyn (Sage) on Jun 13, 2006 at 22:57 UTC
      whose interface I consider more natural, since it more directly mimics find(1),

      While others are thankful that File::Find::Rule means they never have to remember whether "s" is about sockets or symbolic links again :-)

Re: How do you make File::Find a bit less ugly?
by ptum (Priest) on Jun 13, 2006 at 16:28 UTC

    I'm not sure I understand your objection to processing your code inside wanted, but I usually write a file processing subroutine that I call from the bottom of wanted once I know it is a file I want:

    sub wanted { if ($File::Find::name !~ /some regex/) { return; } process_file($File::Find::name); }

    No good deed goes unpunished. -- (attributed to) Oscar Wilde
Re: How do you make File::Find a bit less ugly?
by graff (Chancellor) on Jun 14, 2006 at 03:43 UTC
    I consider the slowness of File::Find and its derivatives to be legendary (based on benchmarks I did here and here). While some of the derivatives offer different API's that might feel more sensible to you depending on what your background is, none of them seem to escape the basic problem of being relatively quite slow on relatively large directory trees.

    That's why I tend to favor using the standard "find" utility from within the perl script, the best idiom being something like:

    my $path = "/whatever/path"; { local $/ = "\0"; open( FIND, "find $path -print0 |" ); while (<FIND>) { chomp; # do something with this name... } }
    with other options added as needed on the "find" command line. Some might think this is "ugly" because it's not pure Perl, and some might think the "find" command is ugly in its own way, but if you've read the man page and know how to use it, it works, it's pretty simple, and nothing else is faster (and some others do seem to agree that "find"-based usage is not as ugly as File::Find).

    Since there are Windows ports of this basic utility, the only barrier to using it would be boneheaded policy constraints against installing such apps on Windows machines. For *n*x users, it seems like a no-brainer.

      Since there are Windows ports of this basic utility, the only barrier to using it would be boneheaded policy constraints against installing such apps on Windows machines.
      I disagree.

      First: These ports tend to use Unix-like syntax for their file paths. Often they're having difficulties with colons or with backslashes as path separators.

      Second: if you want to use the full power of the tool, or even get moderately fluent, there's no command line utility so hard to get used to as find.

        if you want to use the full power of the tool, or even get moderately fluent, there's no command line utility so hard to get used to as find.

        For me, it is Maypole-the-magic. Oh, "comand line utility" would be sed with multiple backslashing-the-backslash version ... then ... i just pull out awk|perl.

Re: How do you make File::Find a bit less ugly?
by bart (Canon) on Jun 14, 2006 at 08:08 UTC
    I don't use wanted. Instead, I use an inline sub for callback.
    find sub { if(wantThis) { print "Here's a file I want: $File::Find::name\n"; } }, @directoryList;