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

Monks,

I came across this problem while using File::Find to find files modified after a certain time. When a file is found, I call a subroutine that itself uses File::Find to find a set of related files that need to be updated. Except it doesn't work. The first 'find' catches the first file, calls the sub, which runs OK, but then the whole thing comes to a stop. No error, just a normal end of program. My question is, why does the first 'find' not continue?

Here's an example script that illustrates the problem:

#!/usr/bin/perl use warnings; use strict; use File::Find (); # See the Cookbook p.324 to sub find (&@) { &File::Find::find } # call find like grep or map my $start = 'd:/temp'; find { find_again($_) if (-d && !/^\./)} $start; sub find_again { my $found = $_; print "First find found: $found\n"; print "Second find found dirs:\n"; find { print "$_\n" if (-d && !/^\./)} $found; } __END__ Directories are: d:/temp/one/a /b /c /two/d /e /f /three/g /h /i OUTPUT: First find found: two Second find found dirs: d e f d e f one a b c three g h i

Two things are going on here that I don't understand. First, why is find_again printing out all these directories? It was only ever called once, with /two as the starting directory. Second, why is find_again only called once? It should (well, what I intended) be called three times, on /one, /two and /three.

I thought the problem might be find changing the current directory, so I cached it at the start of find_again and chdir'd back at the end, but that made no difference.

Can the Monks enlighten me?

Replies are listed 'Best First'.
Re: find loses $found in find
by demerphq (Chancellor) on Sep 13, 2002 at 12:33 UTC
    File::Find maintains its state in package level lexicals. So when you call File::Find from within the wanted() function it resets the state and thus looses track of where it was. I believe that this is implicitly a bug. I contemplated hacking File::Find into a OO version that maintains its state within an object thus allowing you to do what you wanted to do, but I dont have the time to deal with code that crotchety right now (it has gone on my list of things to do though.)

    The way to deal with this is to use something like a breadth-first-traversal pattern. Ie, when you find a file that will cause the files in some other directory to change then push that file onto a list. Find all the files that need such an action _first_ then do the updating afterwards. Something like so: (this says that if we find a file in the first find then we will delete all similarly named files in a different dir)

    # untested example code that could cause all of your .pl files to be + deleted! my @fix; find {no_chdir=>1, wanted=>sub{ -f && m/\.pl$/ && push @fix,[File::Spec->splitpat +h($_)];} },@paths; foreach my $fix (@fix) { find { no_chdir=>1, wanted=>sub{ -f && (File::Spec->splitpath($_))[2] eq $fix->[2] && +unlink $_ } },do{ (my $x=$fix->[0].$fix->[1]) =~s/foo/bar/g; $x}; }
    This is fairly idomatic perl code (sorry) but hopefully you get the idea.

    --- demerphq
    my friends call me, usually because I'm late....

      Well, this is enlightenment - of the can of worms variety! Looking at my code, I 'find' <sigh> that just about my whole script is actually already inside a 'find'.

      So I'll re-work the whole thing using the breadth-first approach, but it's a real shame when such a useful thing as File::Find turns out to have such limitations.

      Can anyone suggest an alternative searching module to File::Find before I start chopping up my script?

      Dave

        Have you checked out the fabtastic File::Find::Rule? From the looks of your conditions it should be a perfect fit!
        HTH

        _________
        broquaint

        Unfortunately I cant suggest anything short of rewriting File::Find to be object oriented.

        Sorry.

        --- demerphq
        my friends call me, usually because I'm late....

Re: find loses $found in find
by bronto (Priest) on Sep 13, 2002 at 12:11 UTC

    BEWARE! find modifies $_. So, if you call a find inside another, you surely get things messed up!

    The wanted() function does whatever verifications you want. $File::Find::dir contains the current directory name, and $_ the current filename within that directory.

    You'd better localize $_ where appropriate!

    Update: I agree with demerphq in his reply: going iteratively is, in this case, the safer thing to do.

    Ciao!
    --bronto

    # Another Perl edition of a song:
    # The End, by The Beatles
    END {
      $you->take($love) eq $you->made($love) ;
    }

Re: find loses $found in find
by PodMaster (Abbot) on Sep 13, 2002 at 11:19 UTC
    That is very bizzare. You don't normally see recursive usage of find, cause find itself is recursive in nature. I suggest you rework you logic.

    ____________________________________________________
    ** The Third rule of perl club is a statement of fact: pod is sexy.

      If there's no other way, I may have to re-think it. But what I'm trying to do seems to me to fit the intent of File::Find, if not its actual capability.

      What I have is a website with static html files generated from templates and content files. Some of the content from some of the content files gets incorporated in the templates of other files. So for instance, if a noticeboard gets updated, new items appear in the noticeboard menus of a set of files. So I want to search for noticeboard files that have been updated, and when I find one, I want to grab all the files in the directory related to that particular noticeboard.

      If I can't get find to do the second search, I guess I'll just have to readdir myself, but it seems a shame not to be able to use File::Find again.

      Dave