Mass renaming, *nix version

Solution 1:

The rename utility will do what you want.

Just use rename 's/Beethoven\ -\ //g' *.mp3

Solution 2:

Like so many things with Linux/Unix, there is more than one way to do it!

Some might use mmv (mass mv).

The rename, or prename command that comes with some Perl packages (newer Ubuntu and Debian) can do this as well.

prename 's/Beethoven\ -\ //g' Beethoven*.mp3

Note that the rename tool on Fedora 10 is not the same program as this and works differently. It is part of a different package, util-linux-ng and can be found here.

I often just go to the shell for loop out of habit (after all such tools didn't always exist).

mkdir -p Beethoven
for x in Beethoven\ -\ *
do
mv "$x" Beethoven/"${x/Beethoven - /}"
done

This will create the Beethoven directory, then move the file to that directory/filename, where the filename has the "Beethoven - " replaced with nothing.

As a test, before:

$ ls
Beethoven - Fur Elise.mp3                 Beethoven - Ode to Joy.mp3
Beethoven - Moonlight Sonata.mp3          Beethoven - Rage Over the Lost Penny.mp3

After:

$ ls Beethoven/
Fur Elise.mp3                 Ode to Joy.mp3
Moonlight Sonata.mp3          Rage Over the Lost Penny.mp3

Solution 3:

Try this solution, implemented using a combination of grep and rename. This is assuming you have a whole bunch of directories that you need to rename. If there are just a few, I'd go with the manual approach, with the "Artist - " pattern hardcoded for each.

# Rename any mp3 files in the directory hierarchy of type "a - b.mp3" to "b.mp3"
find . -name "*.mp3" -exec rename -n -v 's|^(.*/).*-\s*(.*)\s*$|$1$2|g' {} \;

Explanation of find:
The find command finds all the files with .mp3 extension in the current file hierarchy, and calls the argument passed to -exec for each. Note that within -exec, {} refers to the argument passed by find (file path+name). The \; ensures the ; gets passed to rename and instead of indicating the end of the find command.

The command given above has the -n (no action) switch; hence it will only tell you what would happen if you ran it. I recommend you run this command without the -n switch only after you've run it once and are satisfied with the proposed results.

Now for the regexp.

  • ^ matches start of string.
  • $ matches the end of the string.
  • . matches any character.
  • * means zero or more of the preceeding construct. e.g .* means zero or more characters.
  • \s stands for any space character.
  • \S stands for any non-space character.
  • Anything within () gets captured and is reflected as $1, $2, depending on its position.

The regexp:

  • Looks for an optional directory structure at the start, captures this in $1: (.*/)
  • Skips everything till the "-" : .*-
  • Captures the rest of the string, excepting leading and trailing spaces, in $2: (.*)
  • Rewrites the file as $1$2, which should be "/path/to/file/""filename.mp3"

Here's my test run:

/tmp/test$ ls -R
.:
a  b  c  z-unknown.mp3

./a:
a3.mp3                 a - longer title3.mp3  c.mp3   disc2
a - longer title2.mp3  a - long title.mp3     disc 1  the band - the title.mp3

./a/disc 1:
a - title1.mp3  a - title2.mp3

./a/disc2:
a - title1.mp3  a - title2.mp3

./b:
cd - ab - title3.mp3  cd - title1.mp3  cd - title2.mp3  c.mp3

./c:
c-pony2.mp4  c - pony3.mp3  c - pony4.mp3  c-pony.mp3

/tmp/test$ find . -name "*.mp3" -exec rename -v 's|^(.*/).*-\s*(.*)\s*$|$1$2|g' {} \;
./c/c-pony.mp3 renamed as ./c/pony.mp3
./c/c - pony4.mp3 renamed as ./c/pony4.mp3
./c/c - pony3.mp3 renamed as ./c/pony3.mp3
./z-unknown.mp3 renamed as ./unknown.mp3
./a/the band - the title.mp3 renamed as ./a/the title.mp3
./a/a - longer title2.mp3 renamed as ./a/longer title2.mp3
./a/a - longer title3.mp3 renamed as ./a/longer title3.mp3
./a/disc 1/a - title1.mp3 renamed as ./a/disc 1/title1.mp3
./a/disc 1/a - title2.mp3 renamed as ./a/disc 1/title2.mp3
./a/a - long title.mp3 renamed as ./a/long title.mp3
./a/disc2/a - title1.mp3 renamed as ./a/disc2/title1.mp3
./a/disc2/a - title2.mp3 renamed as ./a/disc2/title2.mp3
./b/cd - title1.mp3 renamed as ./b/title1.mp3
./b/cd - title2.mp3 renamed as ./b/title2.mp3
./b/cd - ab - title3.mp3 renamed as ./b/title3.mp3

/tmp/test$ ls -R
.:
a  b  c  unknown.mp3

./a:
a3.mp3  c.mp3  disc 1  disc2  longer title2.mp3  longer title3.mp3  long title.mp3  the title.mp3

./a/disc 1:
title1.mp3  title2.mp3

./a/disc2:
title1.mp3  title2.mp3

./b:
c.mp3  title1.mp3  title2.mp3  title3.mp3

./c:
c-pony2.mp4  pony3.mp3  pony4.mp3  pony.mp3

Regexp's are tricky business, and I've made many a blunder writing them, so I'd welcome any input on the lack of merit of this one. I know I found quite a few testing this.

Solution 4:

I've put together a command-line Java program that makes renaming files (and directories) a breeze, especially for scripting. It's free and open source, so you can modify it to your heart's content:

RenameWand
http://renamewand.sourceforge.net/

Some relevant usage examples:

Move files of the form "<a> - <b>" to their respective directory "<a>":

java -jar RenameWand.jar  "<a> - <b>"  "<a>/<b>"

Sort files by last-modified time, and rename them with a 3-digit number:

java -jar RenameWand.jar  "<a>"  "<3|#FT> <a>"

Rearrange parts of the filename, and change case:

java -jar RenameWand.jar  "<album> - <artist> - <song>.<ext>" 
                          "<artist.upper> <album.title> - <song>.<ext.lower>"

Solution 5:

zmv The zsh renaming tool is very good.

# zmv "programmable rename"
# Replace spaces in filenames with a underline
zmv '* *' '$f:gs/ /_'
# Change the suffix from *.sh to *.pl
zmv -W '*.sh' '*.pl'
# lowercase/uppercase all files/directories
$ zmv '(*)' '${(L)1}' # lowercase
$ zmv '(*)' '${(U)1}' # uppercase

More notes here...