开发者

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 of find().

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜