Solved

Perl recursive rename based on results of directory contents

Posted on 2010-11-19
5
223 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

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Using libpcap/Jpcap to capture and send packets on Solaris version (10/11) Library used: 1.      Libpcap (http://www.tcpdump.org) Version 1.2 2.      Jpcap(http://netresearch.ics.uci.edu/kfujii/Jpcap/doc/index.html) Version 0.6 Prerequisite: 1.      GCC …
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 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.:
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…

760 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

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now