Solved

Perl recursive rename based on results of directory contents

Posted on 2010-11-19
5
231 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Introduction Regular patching is part of a system administrator's tasks. However, many patches require that the system be in single-user mode before they can be installed. A cluster patch in particular can take quite a while to apply if the machine…
There are many situations when we need to display the data in sorted order. For example: Student details by name or by rank or by total marks etc. If you are working on data driven based projects then you will use sorting techniques very frequently.…
Learn several ways to interact with files and get file information from the bash shell. ls lists the contents of a directory: Using the -a flag displays hidden files: Using the -l flag formats the output in a long list: The file command gives us mor…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…

752 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