I’m trying to write a script that will find all files (named *.conf) in a particular directory that have not been modified in n days. Here’s what I have so far:
use File::Find;
$home = $ENV{“HOME”};
$search_dir = $home;
find(&wanted, search_dir);
sub wanted {
if (/.conf/ && int(-M _) > 7) {
print "$File::Find::name
";
}
}
But I’m having 2 problems:
I touched one of the files and it still comes up in the search
If I muck with the directory path (e.g. $search_dir = $home . “/subdir”) it doesn’t find anything
The problem with using the magic _ filehandle is that it will contain the filesystem information for whatever file was last stat()ed. Since you’re not doing any stats or other file tests on your files before using -M, you’ll get the results from some whatever other file happens to be in there.
If you change it to something like this, you should get the results you want:
use strict;
use warnings;
use File::Find;
my $home = $ENV{HOME};
my $search_dir = $home;
find(\&wanted, $search_dir);
sub wanted {
if (/.conf$/ && int(-M $File::Find::name ) > 7) {
print "$File::Find::name
";
}
}
Note that I’ve also turned on the strict and warnings pragmas, which will give you helpful information when debugging, and prevent you from doing naughty stuff like using undeclared variables.
You can even just omit the "File::Find::name" and it will use _ by default, which is the current filename being considered. You could leave out the “int()” as well, since you are comparing with a >.
Note that $_ is just the filename without the directory, so this will only work if you’re searching in the current working directory (or you are keeping track and cwd as necessary.) That’s why it’s better to use $File::Find::name which contains the complete pathname.
The interface to File::Find is full of weird quirks like this and leaves a lot to be desired.
Why is it better? We know we’re using File::Find, so $_ has the filename we want to look at. That’s the way File::Find is designed, to make it easy to use the defaults. I’d make a claim that it is better to use the defaults, easier and cleaner.
Look at the first clause, /.conf$/? Which file is it looking at?
Would you suggest changing that simple expression to the more complicated File::Find::name =~ /\.conf/ ‘just in case’?
I actually wrote my own: File::OFind. Mine is an object oriented version. I never liked File::Find for several reasons. One is the use of global variables. The other is that the “wanted” function either ends up being your entire program, or that you save all of your results in a “global” array in wanted and then use it in your main program.
Mine allows you a more object oriented way of fetching files, and I think it’s a lot easier to understand:
use File::OFind;
use strict;
use warnings;
my $directory = "foo";
my $find = File::OFind->new($directory);
while (my $file = $find->next()) {
next unless ($find->Suffix() eq "conf");
print "The file is $file
";
print "The directory is " . $file->Dirname() . "
";
print "The basename is " . $file->Basename() . "
";
}
If you’re interested in it, you can get it from http://dl.dropbox.com/u/433257/OFind.pm. You can generate the documentation from “perldoc File::OFind” once you put it in the @INC directory. I make no guarantees, and you’ll still owe me a beer (see license).
By the way, don’t forget find2perl which will write the function for you:
$ #find foo -name "*.conf" <-- The "find" command you want
$ find2perl foo -name "*.conf" #Replace "find" with "find2perl"
#! /usr/bin/perl -w
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
if 0; #$running_under_some_shell
use strict;
use File::Find ();
# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.
# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name = *File::Find::name;
*dir = *File::Find::dir;
*prune = *File::Find::prune;
sub wanted;
# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, 'foo');
exit;
sub wanted {
/^.*\.conf\z/s
&& print("$name
");
}
I was thinking about putting on CPAN, but I wasn’t sure if the code is up to snuff for a major distribution. Plus, I’d have to write a set of tests and put together a distribution.
I’d be very grateful if you could help me with the distribution. Thx.