| Share |
Updated: October 6th, 2009
No matter what programming language you use, there comes a time when you need to search for a file somewhere on the file system. Here, I want to talk about accomplishing this task in Perl. There are many ways of doing so, most of them boring, but I want to discuss the fun and elegant way – using File::Find::Rule.
Let me briefly discuss some of the other methods first.
Limited
Using glob() (or <>, TODO verify) you can find files in a single directory, using only the limited shell wildcard support. For example,
1 | my @files = glob("tmp*"); |
Boring
File::Find is the de facto standard for searching in Perl.
This method finds files that end in .pl in "." and "../SomeDir", following symlinks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/usr/bin/perl -w
use File::Find;
use Data::Dumper;
use File::Basename;
my @directories_to_search = (".", "../SomeDir");
my @file_list = ();
find(
{ wanted =>
sub {
if ( basename($File::Find::name) =~ /\.pl$/i )
{
push @file_list, $File::Find::name;
}
},
follow => 1
},
@directories_to_search
);
print Dumper @file_list; |
It works fine, except it's horribly ugly and boring. Let's have a look at something more fun.
The Fun And Elegant Way
File::Find::Rule. Just have a look at this beauty.
Just like above, find all .pl files in "." and "../SomeDir", following symlinks:
1 2 | print Dumper (File::Find::Rule->name("*.pl")->file->extras({ follow => 1 })->
in(".", "../SomeDir")); |
Same as above, except bypass .svn directories (shaves off a ton of time with a lot of directories):
1 2 | print Dumper (File::Find::Rule->not(File::Find::Rule->directory->name('.svn')->
prune->discard)->name("*.pl")->file->extras({ follow => 1 })->in(".", "../SomeDir")); |
Find all .log files that are older than 24 hours in "."
1 2 3 | my $epoch_time_1_day_ago = time() - 60*60*24;
print Dumper (File::Find::Rule->file->name("*.log")->
mtime("<$epoch_time_1_day_ago")->in('.')); |
Be sure to read the File::Find::Rule perldoc for more options and remember: have fun with your code!
Thanks to Perlbuzz and Andy Lester for pointing me to this library a few months ago.
Artem Russakovskii is a San Francisco programmer, blogger, and future millionaire (that last part is in the works). Follow Artem on Twitter (@ArtemR) or subscribe to the RSS feed.
In the meantime, if you found this article useful, feel free to buy me a cup of coffee below.
beer planet is a blog about technology, programming, computers, and geek life. It is run by Artem Russakovskii - a local San Francisco geek who is currently pursuing his own projects and regularly enjoys hacking Android, PHP, CSS, Javascript, AJAX, Perl, and regular expressions, working on Wordpress plugins and tools, tweaking MySQL queries and server settings, administering Linux machines, blogging, learning new things, and other geeky stuff.
Glad you dig the File::Find::Rule, but I've always found it to be a pain to use. Rather than having to use plugin filters, I'd rather just write my own if necessary. So I wrote File::Next.
use File::Next;
my $iter =
File::Next::files( {
file_filter => sub { /\.pl$/ },
descend_filter => sub { $_ ne '.svn' },
}, '.', '../SomeDir' );
while ( my $file = $iter->() ) {
print "$file\n";
}
Like your previous example, that finds all .pl files under . and ../SomeDir, without looking into the .svn directories.
I should note that I recently began work on File-Find-Object-Rule, which is a port of File-Find-Rule to File-Find-Object. This would eventually allow F-F-O-R to overcome some of File::Find::Rule inherent limitations, which are caused due to its reliance on File::Find.
The interface of F-F-Object-Rule still remains pretty much backwards compatible to that of F-F-Rule, but some stuff (like "->start()" and "->match()") have become much saner.