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

Monks, as most of you probably already know, the might of Find::File legendary, but for me it has proved itself a disobedient and unruly servant. It does what I ask it, but carps and barks, late into the night, scaring the wife and children.

Legend has it that helper herbs such as File::Find::Rule and merlyn's File::Finder, or even Find::File alone but with judicious closures, can be used to tame this imp. But so far these have failed me, for my powers of comprehension are weak and the day has been long.

At any rate, here is my working code. It works, but with a warning I can't figure out how to get rid of. Can someone show me how to get this done without warnings, prettily, and robustly? More than one way is welcome.

Thanks for your help.

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use File::Find; my $dir = shift or die "bitte html suchen/ersetzen verzeichnis eingebe +n\n"; opendir(DIR, $dir) or die "html suchen/ersetzen verzeichnis existiert +nicht: $dir"; closedir(DIR); #works, but with warning: #Variable "@myfiles" will not stay shared at fileFindTroubleshooting.p +l line 34. my @files = get_files($dir); foreach (@files) { print "file: $_\n" } sub get_files { my $directory = shift; opendir (DIR, $directory) or die "couldn't open directory: $direct +ory"; my @myfiles; my @directories_to_search = ("$directory"); find(\&wanted, @directories_to_search); sub wanted { my $name = $File::Find::name; if ( $name =~ /html$/i && ! ( $name =~ /\.backup\.before/i ) ) + { $name =~ s|\\|/|; # substitute forward slash for back slas +hes, icky but don't know a better way. #print "name: $name\n"; push @myfiles, $name; } } # end sub wanted return @myfiles; }

Replies are listed 'Best First'.
Re: Help taming File::Find
by Tanktalus (Canon) on Jun 13, 2005 at 17:07 UTC

    It would have been nice if you had included the warning message ;-) Update: Oh, so there it is - quite well hidden, indeed! :-) Confirms my suspicions, too.

    I'm betting it's your sub wanted inside your sub get_files. A slight change may help if this is the case:

    sub get_files { my $directory = shift; opendir (DIR, $directory) or die "couldn't open directory: $direct +ory"; my @myfiles; my @directories_to_search = ("$directory"); my $wanted = sub { my $name = $File::Find::name; if ( $name =~ /html$/i && ! ( $name =~ /\.backup\.before/i ) ) + { $name =~ s|\\|/|; # substitute forward slash for back slas +hes, icky but don't know a better way. #print "name: $name\n"; push @myfiles, $name; } } # end sub wanted find($wanted, @directories_to_search); return @myfiles; }

    Update 2: For those who may have missed it, all I did was move the named "wanted" sub to an anony-sub, and put a ref to it in a lexical variable named "$wanted", and used that. I didn't change any of the rest of tphyahoo's code.

      Thanks Tanktalus. I did include the warning message, though I suppose it was a bit buried. Warning was, "Variable "@myfiles" will not stay shared at fileFindTroubleshooting.p +l line 34."
Re: Help taming File::Find
by runrig (Abbot) on Jun 13, 2005 at 17:15 UTC
    Do not nest the named subs. Move the wanted sub outside of the get_files sub, and make @myfiles a global, or make wanted an anonymous sub, e.g., find(sub { ... }, @directories).
      Yup, that did the trick -- much obliged, runrig. Fixed code is:
      #!/usr/bin/perl use strict; use warnings; use Data::Dumper; use File::Find; my $dir = shift or die "bitte html suchen/ersetzen verzeichnis eingebe +n\n"; opendir(DIR, $dir) or die "html suchen/ersetzen verzeichnis existiert +nicht: $dir"; closedir(DIR); #works, but with warning: #Variable "@myfiles" will not stay shared at fileFindTroubleshooting.p +l line 34. my @files = get_files($dir); foreach (@files) { print "file: $_\n" } sub get_files { my $directory = shift; opendir (DIR, $directory) or die "couldn't open directory: $direct +ory"; my @myfiles; my @directories_to_search = ("$directory"); find( sub { my $name = $File::Find::name; if ( $name =~ /html$/i && ! ( $name =~ /\.backup\.before/i + ) ) { $name =~ s|\\|/|; # substitute forward slash for back +slashes, icky but don't know a better way. #print "name: $name\n"; push @myfiles, $name; } }, @directories_to_search); return @myfiles; }
Re: Help taming File::Find
by graff (Chancellor) on Jun 14, 2005 at 02:42 UTC
    Regarding this line in your code:
    $name =~ s|\\|/|; # substitute forward slash for back slashes, ick +y but don't know a better way.
    It had never occurred to me that File::Find on a Windows box would return file names with backslashes instead of forward slashes. How gauche.

    Anyway, I think a better way to deal with that is:

    $name =~ tr{\\}{/};
    That's a bit less expensive than a regex substitution (and when you're using File::Find, anything you can do to streamline the "wanted" sub will pay off at runtime); also, the "tr" operator will make sure that all backslashes get converted. (As posted, your code will only convert the first backslash in a given file path name, because you forgot to put the "g" at the end.)