Perl recursive rename based on results of directory contents

Hi,
I'm trying to figure out how to rename a bunch of directories and files based on the name of another file inside that directory.
Example:

# ls -al
total 4
drwxrwxrwx 1 root root  160 2010-11-19 23:03 .
drwxrwxrwx 1 root root 4096 2010-11-19 23:31 ..
drwxrwxrwx 1 root root  504 2010-11-19 22:57 The Departed

# cd The\ Departed/
# ls -al
total 57
drwxrwxrwx 1 root root   504 2010-11-19 22:57 .
drwxrwxrwx 1 root root   160 2010-11-19 23:03 ..
-rwxrwxrwx 1 root root 19200 2010-11-19 22:58 cover.jpg
-rwxrwxrwx 1 root root   698 2008-12-31 15:25 folder.wid
-rwxrwxrwx 1 root root 29207 2007-07-26 22:49 folder.wig
-rwxrwxrwx 1 root root     1 2010-11-19 22:59 Some_file name with spaces or parenthesis or a period, etc.avi
-rwxrwxrwx 1 root root   104 2010-11-19 22:58 The Departed (2006) - imdb.nfo


I need to:
1. rename the .avi (or other media type -match an array?) to the same name as the .nfo file (keeping the correct file extension, of course).
2. rename the parent directory the same way.

So the result would be:
# ls -al
total 4
drwxrwxrwx 1 root root  160 2010-11-19 23:03 .
drwxrwxrwx 1 root root 4096 2010-11-19 23:31 ..
drwxrwxrwx 1 root root  504 2010-11-19 22:57 The Departed (2006)

# cd The\ Departed\ (2006)/
# ls -al
total 57
drwxrwxrwx 1 root root   504 2010-11-19 22:57 .
drwxrwxrwx 1 root root   160 2010-11-19 23:03 ..
-rwxrwxrwx 1 root root 19200 2010-11-19 22:58 cover.jpg
-rwxrwxrwx 1 root root   698 2008-12-31 15:25 folder.wid
-rwxrwxrwx 1 root root 29207 2007-07-26 22:49 folder.wig
-rwxrwxrwx 1 root root     1 2010-11-19 22:59 The Departed (2006).avi
-rwxrwxrwx 1 root root   104 2010-11-19 22:58 The Departed (2006) - imdb.nfo

Make sense?
I would also like to do it recursively and (hopefully) not screw it all up :-)
cdukesAsked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
wilcoxonConnect With a Mentor Commented:
Looks like it.  This should work now...
#!/usr/bin/perl

use strict;
use warnings;
use File::Find ();
use File::Basename;

# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.

# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name   = *File::Find::name;
*dir    = *File::Find::dir;
*prune  = *File::Find::prune;

# change/add extensions to the list to pick up (or exclude) more
my @types = (qw(avi mpg mpeg));
#my @searchdirs = (qw(.));
my @searchdirs = (qw(test));

# Traverse desired filesystems
File::Find::finddepth({wanted => \&wanted}, @searchdirs);
exit;

sub wanted {
    my $dir = $_;
    if (-d $dir) {
        opendir DIR, $dir or die "could not open dir $dir: $!";
        my @files = readdir DIR;
        closedir DIR;
        my @nfo = grep m{\.nfo$}, @files;
        my $base;
        return unless @nfo;
        if (@nfo > 1) {
            die "found multiple .nfo files in $dir - don't know what to do";
        } else {
            ($base) = fileparse($nfo[0], 'nfo');
            $base =~ s{\s+-\s+imdb\.$}{};
            my $rx = join '|', @types;
            my @media = grep m{\.(?:$rx)$}, @files;
            die "found multiple media files (@media) in $dir - don't know what to do" if (@media > 1);
            my ($junk1, $junk2, $suf) = fileparse($media[0], @types);
            rename "$dir/$media[0]", "$dir/$base$suf"
                or die "could not rename $dir/$media[0] to $dir/$base.$suf: $!";
        }
        my $pdir = $dir;
        if ($pdir =~ m{/}) {
            $pdir =~ s{/[^/]+/?$}{};
        } else {
            $pdir = '.';
        }
        rename $dir, "$pdir/$base" or die "could not rename dir $dir to $pdir/$base: $!";
    }
}

Open in new window

0
 
wilcoxonCommented:
I think this will do what you want.  I would suggest copying part of your directory structure and testing it there (in case it screws up something)...

If there are problems, let me know and I'll correct them later today.
#!/usr/local/bin/perl

use strict;
use warnings;
use File::Find ();

# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.

# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name   = *File::Find::name;
*dir    = *File::Find::dir;
*prune  = *File::Find::prune;

# change/add extensions to the list to pick up (or exclude) more
my @types = (qw(avi mpg mpeg));

# Traverse desired filesystems
File::Find::finddepth({wanted => \&wanted}, $your_tree_root_path);
exit;

sub wanted {
    if (-d $dir) {
        opendir DIR, $dir or die "could not open dir $dir: $!";
        my @files = readdir DIR;
        closedir DIR;
        my @nfo = grep m{\.nfo$}, @files;
        my ($base, $path)
        if (@nfo > 1) {
            die "found multiple .nfo files in $dir - don't know what to do";
        } elsif (@nfo == 1) {
            ($base, $path) = fileparse($nfo[0], 'nfo');
            my $rx = join '|', @types;
            my @media = grep m{\.(?:$rx)$}, @files;
            die "found multiple media files (@media) in $dir - don't know what to do" if (@media > 1);
            my ($junk1, $junk2, $suf) = fileparse($media[0], @types);
            rename $media[0], "$path/$base.$suf"
                or die "could not rename $media[0] to $path/$base.$suf: $!";
        }
        my $pdir = $dir;
        $pdir =~ s{/[^/]+/?$}{};
        rename $dir, "$pdir/$base" or die "could not rename dir $dir to $pdir/$base: $!";
    }
}

Open in new window

0
 
wilcoxonCommented:
Minor correction as I just noticed your example .nfo has " - imdb" but your renamed one does not...

Add this after line 33:

$base =~ s{\s+-\s+imdb$}{};
0
 
cdukesAuthor Commented:
Had to make a few slight changes, but here's what I get:
could not rename The.Departed  (2006).avi to .//The Departed    (2006) - imdb.avi: No such file or directory at ./p.pl line 42.

It looks like it's not detecting the full path of the .nfo file, just the file name itself?

New code below.
#!/usr/bin/perl

use strict;
use warnings;
use File::Find ();
use File::Basename;

# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.

# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name   = *File::Find::name;
*dir    = *File::Find::dir;
*prune  = *File::Find::prune;

# change/add extensions to the list to pick up (or exclude) more
my @types = (qw(avi mpg mpeg));
my @searchdirs = (qw(.));

# Traverse desired filesystems
File::Find::finddepth({wanted => \&wanted}, @searchdirs);
exit;

sub wanted {
    my $dir = $_;
    if (-d $dir) {
        opendir DIR, $dir or die "could not open dir $dir: $!";
        my @files = readdir DIR;
        closedir DIR;
        my @nfo = grep m{\.nfo$}, @files;
        my ($base, $path);
        if (@nfo > 1) {
            die "found multiple .nfo files in $dir - don't know what to do";
        } elsif (@nfo == 1) {
            ($base, $path) = fileparse($nfo[0], 'nfo');
            $base =~ s{\s+-\s+imdb$}{};
            my $rx = join '|', @types;
            my @media = grep m{\.(?:$rx)$}, @files;
            die "found multiple media files (@media) in $dir - don't know what to do" if (@media > 1);
            my ($junk1, $junk2, $suf) = fileparse($media[0], @types);
            rename $media[0], "$path/$base$suf"
                or die "could not rename $media[0] to $path/$base$suf: $!";
        }
        my $pdir = $dir;
        $pdir =~ s{/[^/]+/?$}{};
        rename $dir, "$pdir/$base" or die "could not rename dir $dir to $pdir/$base: $!";
    }
}

Open in new window

0
 
cdukesAuthor Commented:
Awesome, thank you SO much!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.