Solved

Perl recursive rename based on results of directory contents

Posted on 2010-11-19
5
227 Views
Last Modified: 2012-06-21
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 :-)
0
Comment
Question by:cdukes
  • 3
  • 2
5 Comments
 
LVL 26

Expert Comment

by:wilcoxon
ID: 34180160
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
 
LVL 26

Expert Comment

by:wilcoxon
ID: 34180163
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
 

Author Comment

by:cdukes
ID: 34180730
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
 
LVL 26

Accepted Solution

by:
wilcoxon earned 500 total points
ID: 34181308
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
 

Author Closing Comment

by:cdukes
ID: 34181848
Awesome, thank you SO much!
0

Featured Post

Gigs: Get Your Project Delivered by an Expert

Select from freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely and get projects done right.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I have been running these systems for a few years now and I am just very happy with them.   I just wanted to share the manual that I have created for upgrades and other things.  Oooh yes! FreeBSD makes me happy (as a server), no maintenance and I al…
A year or so back I was asked to have a play with MongoDB; within half an hour I had downloaded (http://www.mongodb.org/downloads),  installed and started the daemon, and had a console window open. After an hour or two of playing at the command …
Learn how to find files with the shell using the find and locate commands. Use locate to find a needle in a haystack.: With locate, check if the file still exists.: Use find to get the actual location of the file.:
This video shows how to set up a shell script to accept a positional parameter when called, pass that to a SQL script, accept the output from the statement back and then manipulate it in the Shell.

785 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question