Perl: Recursively rename all files and directories
I need to recursively rename every file and directory. I convert spaces to underscores and make all file/directory names to lowercase. How can I make the following script rename all files in one run? Currently the script needs to be run several times before all the files/directories are converted. The code is below:
#!/usr/bin/perl
use File::Find;
$input_file_dir = $ARGV[0];
sub process_file {
$clean_name=lc($_);
$clean_name=~s/\s/_/g;
rename($_,$clean_name);
开发者_运维问答 print "file/dir name: $clean_name\n";
}
find(\&process_file, $input_file_dir);
You either need to specify bydepth => 1
in the options you pass to find or call finddepth
. From perldoc File::Find:
bydepth
Reports the name of a directory only AFTER all its entries have been reported. Entry point
finddepth()
is a shortcut for specifying{ bydepth => 1 }
in the first argument offind()
.
However, you still need to decide how to deal with naming clashes because rename will clobber the target if the target exists.
#!/usr/bin/perl
use strict; use warnings;
use File::Find;
finddepth(\&process_file, $_) for @ARGV;
if you are open to other approaches, here's a Python solution
import os
for R,DIR,FILES in os.walk("/mypath",topdown=False):
for file in FILES:
newfile=file.lower().replace(" ","_")
new_file_name=os.path.join(R,newfile)
os.rename( os.path.join(R,file) , new_file_name)
for dir in DIR:
newdir=dir.lower().replace(" ","_")
new_dir_name=os.path.join(R,newdir)
os.rename( os.path.join(R,dir) , new_dir_name)
You can rename files before traversing through a directory.
find({
preprocess => sub {
for (@_) {
my $oldname = $_;
$_ = lc;
s/\s/_/g;
rename $oldname => $_;
}
return @_;
},
wanted => sub {
print "$File::Find::name was already renamed\n";
},
},
@dirs
);
Or you could delay the renames until after traversal has occurred.
finddepth(sub {
print "in $File::Find::dir, renaming $_\n";
my $newname = lc;
$newname =~ s/\s/_/g;
rename $_ => $newname;
},
@dirs
);
The problem you are encountering is because
find
finds directory "Abc Def"
find
calls wanted("Abc Def")
rename "Abc Def" => "abc_def"
find
tries to enter "Abc Def"
, which does not exist anymore
so everything underneath does not get processed
精彩评论