You are not logged in.

#1 2010-07-19 18:34:48

gogi-goji
Member
From: Canada
Registered: 2009-10-20
Posts: 73
Website

Renaming files with perl and find

In order to clean up the filenames in my music directory, I've been working on writing a script to rename files automagically for me.  However, I'm having troubles with recursion in this script.  If it changes the name of a directory, it does not alter the names of files or directories beneath the changed directory.  To demonstrate the problem, I set up a test directory, where the only things that will be changed are spaces in filenames.

The script (file-rename.pl):

#!/usr/bin/perl -w

use strict;

chomp(@ARGV = <STDIN>) unless @ARGV;

foreach my $filename (@ARGV)
{
    my $orig_filename = $filename;
    
    $filename =~ tr/ /\-/;

    $filename =~ tr/[{</(/;

    $filename =~ tr/]}>/)/;

    $filename =~ s/&/and/;

    $filename =~ tr{ a-zA-Z0-9.\-/_()}{}cd;
    
    #$filename =~ tr/A-Z/a-z/;

    unless ($filename eq $orig_filename)
    {  
        print "About to rename $orig_filename to $filename\n";
        if (-e $filename)
        {
            print "There already exists a file named $filename\n";
            print "Skipping the rename – you will have to do it manually\n";
        }
        else
        {
            rename($orig_filename, $filename);
        }  
    }
}

The test directory (before):

$ ls -R
.:
artist
artist with spaces in name

./artist:
album
album with spaces in name

./artist/album:
song
song with spaces

./artist/album with spaces in name:
song
song with spaces

./artist with spaces in name:
album
album with spaces in name

./artist with spaces in name/album:
song
song with spaces

./artist with spaces in name/album with spaces in name:
song
song with spaces

The find command to run the script and rename the files and directories:

$ find . -depth -print0 | xargs -0 /location/of/file-rename.pl

The test directory (after)

$ ls -R
.:
artist
artist-with-spaces-in-name
file-rename-output.txt

./artist:
album
album-with-spaces-in-name

./artist/album:
song
song-with-spaces

./artist/album-with-spaces-in-name:
song
song with spaces

./artist-with-spaces-in-name:
album
album with spaces in name

./artist-with-spaces-in-name/album:
song
song with spaces

./artist-with-spaces-in-name/album with spaces in name:
song
song with spaces

The output of the script:

About to rename ./artist with spaces in name/album with spaces in name/song with spaces to ./artist-with-spaces-in-name/album-with-spaces-in-name/song-with-spaces
About to rename ./artist with spaces in name/album with spaces in name/song to ./artist-with-spaces-in-name/album-with-spaces-in-name/song
About to rename ./artist with spaces in name/album with spaces in name to ./artist-with-spaces-in-name/album-with-spaces-in-name
About to rename ./artist with spaces in name/album/song with spaces to ./artist-with-spaces-in-name/album/song-with-spaces
About to rename ./artist with spaces in name/album/song to ./artist-with-spaces-in-name/album/song
About to rename ./artist with spaces in name/album to ./artist-with-spaces-in-name/album
About to rename ./artist with spaces in name to ./artist-with-spaces-in-name
About to rename ./artist/album with spaces in name/song with spaces to ./artist/album-with-spaces-in-name/song-with-spaces
About to rename ./artist/album with spaces in name/song to ./artist/album-with-spaces-in-name/song
About to rename ./artist/album with spaces in name to ./artist/album-with-spaces-in-name
About to rename ./artist/album/song with spaces to ./artist/album/song-with-spaces

I don't understand why this is only renaming the highest level that needs renaming, rather than renaming all of the files and directories.  Why isn't this code doing what I want it to, and what do I need to change so that it alters the names of all files and directories in one fell swoop?


My (sporadically updated) blog
My miscellaneous dotfiles

Offline

#2 2010-07-19 19:17:46

ber_t
Member
From: Berlin, Germany
Registered: 2010-03-10
Posts: 214
Website

Re: Renaming files with perl and find

The Rename command can only rename one part of a path at a time. So trying to rename "./artist with spaces in name/album with spaces in name/song with spaces" to "./artist-with-spaces-in-name/album-with-spaces-in-name/song-with-spaces" results in an error. Next time make sure to check return values of functions like rename. A simple "... or die;" would be enough.

The easiest solution to your problem would be an outer loop, maybe in the shell. Something like:

for i in `seq 1 $MAX`; do find . -maxdepth $i | /location/of/file-rename.pl; done

Just make sure to determine the maximum depth ($MAX) of your directory structure before.

EDIT: Please just forget what I've said about using xargs...

Last edited by ber_t (2010-07-19 19:22:08)

Offline

#3 2010-07-19 21:42:03

brisbin33
Member
From: boston, ma
Registered: 2008-07-24
Posts: 1,796
Website

Re: Renaming files with perl and find

i have a similar script in bash.  what you should do is the following,

split the arguments into dirname/basename, define just the newbasename (leaving dirname alone), and mv/rename dirname/basename to dirname/newbasename.

by passing the arguments in with the -depth flag, you'll work your way up the tree operating on one level at a time, renaming everything correctly.

for reference:

translate() {
  tr -d '\n' | tr -d '\t' | tr -d \' | tr -d \" |\
    sed -r -e 's/.*/\L&/g' \
           -e 's/[ -]/_/g' \
           -e 's/_+/_/g'   \
           -e 's/^_|_$//g'
}

rfile() {
  local dir old new

  dir="$(dirname "$1")"
  old="$(basename "$1")"
  new="$(echo $old | translate)"

  if [[ "$old" != "$new" ]]; then
    if $fake; then
      echo "$dir/$old --> $dir/$new"
    else
      mv -iv "$dir/$old" "$dir/$new"
    fi
  fi
}

rdir() {
  local dir

  while IFS='' read -d '' -r dir; do
    rfile "$dir"
  done < <(find "$1" -depth -print0)
}

(just the interesting parts)

Last edited by brisbin33 (2010-07-19 21:43:07)

Offline

Board footer

Powered by FluxBB