使用 Perl 查找文件 [英] Finding files with Perl
问题描述
File::Find
and the wanted
subroutine
This question is much simpler than the original title ("prototypes and forward declaration of subroutines"!) lets on. I'm hoping the answer, however simple, will help me understand subroutines/functions, prototypes and scoping and the File::Find
module.
With Perl, subroutines can appear pretty much anywhere and you normally don't need to make forward declarations (except if the sub declares a prototype, which I'm not sure how to do in a "standard" way in Perl). For what I usually do with Perl there's little difference between these different ways of running somefunction
:
sub somefunction; # Forward declares the function
&somefunction;
somefunction();
somefunction; # Bare word warning under `strict subs`
I often use find2perl
to generate code which I crib/hack into parts of scripts. This could well be bad style and now my dirty laundry is public, but so be it :-) For File::Find
the wanted
function is a required subroutine - find2perl
creates it and adds sub wanted;
to the resulting script it creates. Sometimes, when I edit the script I'll remove the "sub
" from sub wanted
and it ends up as &wanted;
or wanted();
. But without the sub wanted;
forward declaration form I get this warning:
Use of uninitialized value $_ in lstat at findscript.pl line 29
My question is: why does this happen and is it a real problem? It is "just a warning", but I want to understand it better.
- The documentation and code say
$_
is localized inside ofsub wanted {}
. Why would it be undefined if I usewanted();
instead ofsub wanted;
? - Is
wanted
using prototypes somewhere? Am I missing something obvious inFind/File.pm
? - Is it because
wanted
returns a code reference? (???)
My guess is that the forward declaration form "initializes" wanted
in some way so that the first use doesn't have an empty default variable. I guess this would be how prototypes - even Perl prototypes, such as they exist - would work as well. I tried grepping through the Perl source code to get a sense of what sub
is doing when a function is called using sub function
instead of function()
, but that may be beyond me at this point.
Any help deepening (and speeding up) my understanding of this is much appreciated.
EDIT: Here's a recent example script here on Stack Overflow that I created using find2perl
's output. If you remove the sub
from sub wanted;
you should get the same error.
EDIT: As I noted in a comment below (but I'll flag it here too): for several months I've been using Path::Iterator::Rule
instead of File::Find
. It requires perl >5.10
, but I never have to deploy production code at sites with odd, "never upgrade", 5.8.*
only policies so Path::Iterator::Rule
has become one of those modules I never want to do with out. Also useful is Path::Class
. Cheers.
I'm not a big fan of File::Find. It just doesn't work right. The find
command doesn't return a list of files, so you either have to use a non-local array variable in your find
to capture your list of files you've found (not good), or place your entire program in your wanted subroutine (even worse). Plus, the separate subroutine means that your logic is separate from your find
command. It's just ugly.
What I do is inline my wanted
subroutine inside my find
command. Subroutine stays with the find. Plus, my non-local array variable is now just part of my find
command and doesn't look so bad
Here's how I handle the File::Find
-- assuming I want files that have a .pl
suffix:
my @file_list;
find ( sub {
return unless -f; #Must be a file
return unless /.pl$/; #Must end with `.pl` suffix
push @file_list, $File::Find::name;
}, $directory );
# At this point, @file_list contains all of the files I found.
This is exactly the same as:
my @file_list;
find ( &wanted, $directory );
sub wanted {
return unless -f;
return unless /.pl$/;
push @file_list, $File::Find::name;
}
# At this point, @file_list contains all of the files I found.
In lining just looks nicer. And, it keep my code together. Plus, my non-local array variable doesn't look so freaky.
I also like taking advantage of the shorter syntax in this particular way. Normally, I don't like using the inferred $_
, but in this case, it makes the code much easier to read. My original Wanted is the same as this:
sub wanted {
my $file_name = $_;
if ( -f $file_name and $file_name =~ /.pl$/ ) {
push @file_list, $File::Find::name;
}
}
File::Find
isn't that tricky to use. You just have to remember:
- When you find a file you don't want, you use
return
to go to the next file. $_
contains the file name without the directory, and you can use that for testing the file.- The file's full name is
$File::Find::name
. - The file's directory is
$File::Find::dir
.
And, the easiest way is to push the files you want into an array, and then use that array later in your program.
这篇关于使用 Perl 查找文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!