# perl File::Find confusion

**URL:** <https://boards.straightdope.com/t/perl-file-find-confusion/565846>\
**Category:** Factual Questions\
**Created:** [December 30, 2010, 5:34pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846 "2010-12-30T17:34:23Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![NoCoolUserName](https://avatars.discourse-cdn.com/v4/letter/n/5fc32e/32.png) [@NoCoolUserName](https://boards.straightdope.com/u/NoCoolUserName)\
**Post date:** [December 30, 2010, 5:34pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/1 "2010-12-30T17:34:23Z")

</div>

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:

1. I touched one of the files and it still comes up in the search
2. If I muck with the directory path (e.g. $search\_dir = $home . “/subdir”) it doesn’t find anything

Can someone help?

---

<div class="post-metadata">

**Author:** ![friedo](https://avatars.discourse-cdn.com/v4/letter/f/8edcca/32.png) [@friedo](https://boards.straightdope.com/u/friedo)\
**Post date:** [December 30, 2010, 5:59pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/2 "2010-12-30T17:59:26Z")

</div>

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:

```auto

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.

---

<div class="post-metadata">

**Author:** ![Kyrie\_Eleison](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/kyrie_eleison/32/7682_2.png) [@Kyrie\_Eleison](https://boards.straightdope.com/u/Kyrie_Eleison)\
**Post date:** [December 30, 2010, 6:05pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/3 "2010-12-30T18:05:54Z")

</div>

> [@NoCoolUserName](#):
>
> 1. I touched one of the files and it still comes up in the search

You left out a “$”, and for correctness, you want to escape the “.” in your regexp. You want this:

```auto

  if (/.conf$/ && int(-M _) > 7) {

```

To be this:

```auto

  if (/\.conf$/ && int(-M $_) > 7) {

```

On preview: **Friedo** ’s suggestion to use $File::Find::name instead is a good one.

---

<div class="post-metadata">

**Author:** ![SmartAlecCat](https://avatars.discourse-cdn.com/v4/letter/s/67e7ee/32.png) [@SmartAlecCat](https://boards.straightdope.com/u/SmartAlecCat)\
**Post date:** [December 30, 2010, 6:08pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/4 "2010-12-30T18:08:39Z")

</div>

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 \>.

BTW, [http://perlmonks.org/](http://perlmonks.org/) is the best place to ask perl questions.

---

<div class="post-metadata">

**Author:** ![friedo](https://avatars.discourse-cdn.com/v4/letter/f/8edcca/32.png) [@friedo](https://boards.straightdope.com/u/friedo)\
**Post date:** [December 30, 2010, 6:15pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/5 "2010-12-30T18:15:21Z")

</div>

> [@SmartAlecCat](#):
>
> 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.

---

<div class="post-metadata">

**Author:** ![NoCoolUserName](https://avatars.discourse-cdn.com/v4/letter/n/5fc32e/32.png) [@NoCoolUserName](https://boards.straightdope.com/u/NoCoolUserName)\
**Post date:** [December 30, 2010, 6:57pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/6 "2010-12-30T18:57:39Z")

</div>

I believe it’s working now. Thanks! The magic “\_” was the problem.

---

<div class="post-metadata">

**Author:** ![SmartAlecCat](https://avatars.discourse-cdn.com/v4/letter/s/67e7ee/32.png) [@SmartAlecCat](https://boards.straightdope.com/u/SmartAlecCat)\
**Post date:** [December 30, 2010, 7:16pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/7 "2010-12-30T19:16:59Z")

</div>

> [@friedo](#):
>
> 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.)

Which File::Find does for you.

> [@friedo](#):
>
> That’s why it’s better to use $File::Find::name which contains the complete pathname.

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’?

---

<div class="post-metadata">

**Author:** ![friedo](https://avatars.discourse-cdn.com/v4/letter/f/8edcca/32.png) [@friedo](https://boards.straightdope.com/u/friedo)\
**Post date:** [December 30, 2010, 10:10pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/8 "2010-12-30T22:10:25Z")

</div>

> [@SmartAlecCat](#):
>
> Would you suggest changing that simple expression to the more complicated  
> File::Find::name =~ /\.conf/ ‘just in case’?

No, I’d suggest not using File::Find at all, because I can obviously never remember what it puts in $\_ and how it treats the cwd 😃

I like File::Find::Rule better.

---

<div class="post-metadata">

**Author:** ![SmartAlecCat](https://avatars.discourse-cdn.com/v4/letter/s/67e7ee/32.png) [@SmartAlecCat](https://boards.straightdope.com/u/SmartAlecCat)\
**Post date:** [December 30, 2010, 11:15pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/9 "2010-12-30T23:15:54Z")

</div>

> [@friedo](#):
>
> I like File::Find::Rule better.

I’ll second that.. 🙂

---

<div class="post-metadata">

**Author:** ![qazwart](https://avatars.discourse-cdn.com/v4/letter/q/5fc32e/32.png) [@qazwart](https://boards.straightdope.com/u/qazwart)\
**Post date:** [December 30, 2010, 11:40pm UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/10 "2010-12-30T23:40:13Z")

</div>

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:

```auto

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](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:

```auto

$ #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
");
}

```

---

<div class="post-metadata">

**Author:** ![friedo](https://avatars.discourse-cdn.com/v4/letter/f/8edcca/32.png) [@friedo](https://boards.straightdope.com/u/friedo)\
**Post date:** [December 31, 2010, 12:09am UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/11 "2010-12-31T00:09:38Z")

</div>

**qazwart** , you should put that on CPAN. I’ll help you create a distribution for it if you haven’t done that before.

---

<div class="post-metadata">

**Author:** ![qazwart](https://avatars.discourse-cdn.com/v4/letter/q/5fc32e/32.png) [@qazwart](https://boards.straightdope.com/u/qazwart)\
**Post date:** [December 31, 2010, 2:48am UTC](https://boards.straightdope.com/t/perl-file-find-confusion/565846/12 "2010-12-31T02:48:52Z")

</div>

> [@friedo](#):
>
> **qazwart** , you should put that on CPAN. I’ll help you create a distribution for it if you haven’t done that before.

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.
