Re: problems returning from recursive subroutine
by benn (Vicar) on Apr 18, 2003 at 09:01 UTC
|
the problem is that your readdir loop is getting trashed by the re-entrant sub - each time you recurse, you lose the previous directory loop. Something like this works (tested too!)...
#!/usr/bin/perl -w
use strict;
lc_filenames(shift @ARGV);
sub lc_filenames{
my($dir)=@_;
$dir||=".";
opendir DIR, $dir;
my @files = readdir DIR;
closedir DIR;
foreach my $file(@files) {
next if ($file =~ /^\.+$/);
lc_filenames("$dir/$file") if(-d ("$dir/$file"));
rename("$dir/$file", "$dir/".lc($file)) if($file=~/[A-Z]/);
}
}
Cheers,
Ben
| [reply] [d/l] |
Re: problems returning from recursive subroutine
by Jenda (Abbot) on Apr 18, 2003 at 09:37 UTC
|
The DIR handle is GLOBAL. Therefore the recursive call clobbers (overwrites) the "parent"'s dirhandle so when the call returns the dirhandle is at the end of the list and therefore cannot return anything.
You have to add local *DIR;
into the subroutine, above the opendir or use a lexical dirhandle:
sub lc_filenames{
my($dir)=@_;
$dir||=".";
opendir my $DIR, $dir;
while(defined(my $file=readdir $DIR)){
...
This syntax will not work in older Perls! You may need to add use FileHandle; and declare the $DIR above the opendir like this:
my $DIR;
opendir $DIR, $dir;
Another nit. You do not close the dirhandle! You should!
Jenda
Always code as if the guy who ends up maintaining your code
will be a violent psychopath who knows where you live.
-- Rick Osborne
Edit by castaway: Closed small tag in signature | [reply] [d/l] [select] |
|
|
You do not close the dirhandle! You should!
Why? Perl will close the handle automatically when it
goes out of scope. Closing it yourself only makes sense
if you are interested in the return value, and are going
to do something different depending on the value. Would
you expect a close() to fail, and if it fails, what action
would you take?
Abigail
| [reply] |
|
|
I would offer that the reason to explicitly close everything is to tell your maintainer that you've done so. Another, more important, reason is the same reason I always put the trailing comma in a list of stuff - what if I add more stuff?!? Then, the implicit close happens later, and that may not be good.
Of course, best, in my opinion, is to limit your exposure to connections like handles and $sth's to as small a block as possible, just in case.
------ We are the carpenters and bricklayers of the Information Age. Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement. Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.
| [reply] |
|
|
|
|
|
|
|
In fact close can indeed fail because, for instance, the disk is full and so it cannot flush buffers. In which case you probably want to report that and possibly want to stop processing. And closing a pipe can give you all sorts of error information.
Also according to the documentation for 5.6.1, an explicit close resets $. while an implicit one due to a following open does not. If you are reporting $. and want that to be accurate, then it is better to do an explicit close whether or not you pay attention to its return value.
| [reply] |
|
|
|
|
•Re: problems returning from recursive subroutine
by merlyn (Sage) on Apr 18, 2003 at 11:11 UTC
|
And of course, the proper way to do this is to use File::Find. Yours improperly follows symlinks, which means you could get in to an infinite loop if the symlink points to a directory somewhere upward in the tree.
Using proper technology, your code becomes simply:
use File::Find;
finddepth sub {
return if /^\./;
my $new = lc $_;
return if $new eq $_ or -e $new;
rename $_, $new or warn "Cannot rename $File::Find::name to $File::F
+ind::dir/$new: $!";
}, $ARGV[0] || ".";
-- Randal L. Schwartz, Perl hacker
Be sure to read my standard disclaimer if this is a reply. | [reply] [d/l] |
|
|
Without knowing more about the context, it's hard to say
whether following symlink is appropriate or not. For all
we know, your solution is wrong because it improperly
does not follow symlinks, which means part of the
directory structure isn't updated.
Abigail
| [reply] |
|
|
If symlinks must be followed, then still the safest is to use modern technology:
use File::Find;
finddepth {
wanted => sub {
return if /^\./;
my $new = lc $_;
return if $new eq $_ or -e $new;
rename $_, $new or warn "Cannot rename $File::Find::name to $File:
+:Find::dir/$new: $!";
},
follow => 1,
}, $ARGV[0] || ".";
-- Randal L. Schwartz, Perl hacker
Be sure to read my standard disclaimer if this is a reply. | [reply] [d/l] |
Re: problems returning from recursive subroutine
by Abigail-II (Bishop) on Apr 18, 2003 at 08:46 UTC
|
| [reply] |
Re: problems returning from recursive subroutine (file::find instead)
by Aristotle (Chancellor) on Apr 18, 2003 at 11:21 UTC
|
As long as it's the results you're after, I wouldn't bother debugging it when a working alternative is so simple to write.
use File::Find;
push @ARGV, '.' unless @ARGV;
finddepth(sub {
my $new = lc;
return if $new eq $_;
rename $_, $new unless -e $new;
}, @ARGV);
Using finddepth rather than find is important because directories will otherwise be renamed before File::Find (at that point futily) attempts to recurse into them.
Makeshifts last the longest. | [reply] [d/l] |
|
|
rename $_, $new unless -e $new;
Funny ;D On some FSs, -e 'foo' == -e 'Foo', so you should take that out.
Besides, it's redundant on FSs that are case sensitive.
update: heh, here's a different approach then
use File::Find;
@ARGV ||= '.';
find( { preprocess => \&pp }, @ARGV );
sub pp {
for(@_){
my $new = lc $_;
next if $new eq $_;
unless(-e $new){
rename $_, $new;
$_ = $new;
}
}
return @_;
}
MJD says you
can't just make shit up and expect the computer to know what you mean, retardo!
I run a Win32 PPM
repository for perl 5.6x+5.8x. I take requests.
** The Third rule of perl club is a statement of fact: pod is sexy.
|
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
$ perl -e '@ARGV ||= "."'
Can't modify array dereference in logical or assignment (||=) at -e li
+ne 1, at EOF
Don't get your contexts mixed up. The left side of a logical operator is always in scalar context. Also don't forget - rename may fail, so if you're going to return the new filename you should be sure the renaming succeeded. Overall this seems more like a job for map:
sub pp {
map {
my $new = lc $_;
($new eq $_ or -e $new)
? $_
: rename($_, $new) ? $new : $_;
} @_;
}
Makeshifts last the longest. | [reply] [d/l] [select] |
Re: problems returning from recursive subroutine
by zby (Vicar) on Apr 18, 2003 at 08:02 UTC
|
If the directory did indeed have some uppercase letters than this call: lc_filenames("$dir/$file"); would process a nonexisting directory (it is after you renamed the "$dir/$file").
| [reply] [d/l] [select] |
|
|
Wouldn't the line above it:
$file = $newname;
solve that by making $file (which in this case is a directory) the lowercase version? Or perhaps there's something else I'm not seeing. | [reply] [d/l] |
|
|
| [reply] |