views:

455

answers:

8

I have thousands of mp3s inside a complex folder structure which resides within a single folder. I would like to move all the mp3s into a single directory with no subfolders. I can think of a variety of ways of doing this using the find command but one problem will be duplicate file names. I don't want to replace files since I often have multiple versions of a same song. Auto-rename would be best. I don't really care how the files are renamed.

Does anyone know a simple and safe way of doing this?

+1  A: 

I'd tend to do this in a simple script rather than try to fit in in a single command line.

For instance, in python, it would be relatively trivial to do a walk() through the directory, copying each mp3 file found to a different directory with an automatically incremented number.

If you want to get fancier, you could have a dictionary of existing file names, and simply append a number to the duplicates. (the index of the dictionary being the file name, and the value being the number of files found so far, which would become your suffix)

Kena
A: 
find /path/to/mp3s -name *.mp3 -exec mv \{\} /path/to/target/dir \;
Aaron Maenpaa
this won't deal with the duplicate filenames issue mentioned in the question
Glen
+1  A: 

At the risk of many downvotes, a perl script could be written in short time to accomplish this.

Pseudocode:

while (-e filename)
     change filename to filename . "1";
belgariontheking
+2  A: 

bash like pseudocode:

for i in `find . -name "*.mp3"`; do
    NEW_NAME = `basename $i`
    X=0
    while ! -f move_to_dir/$NEW_NAME
        NEW_NAME = $NEW_NAME + incr $X

    mv $i $NEW_NAME
done
Glen
+2  A: 
#!/bin/bash
NEW_DIR=/tmp/new/

IFS="
"; for a in `find . -type f `
    do
    echo "$a"
    new_name="`basename $a`"
    while test -e "$NEW_DIR/$new_name"
            do
            new_name="${new_name}_"
            done
    cp "$a" "$NEW_DIR/$new_name"
    done
mateusza
+3  A: 

You could change a a/b/c.mp3 path into a - b - c.mp3 after copying. Here's a solution in Bash:

find srcdir -name '*.mp3' -printf '%P\n' |
    while read i; do
        j="${i//\// - }"
        cp -v "srcdir/$i" "dstdir/$j"
    done

And in a shell without ${//} substitution:

find srcdir -name '*.mp3' -printf '%P\n' |
    sed -e 'p;s:/: - :g' |
    while read i; do
        read j
        cp -v "srcdir/$i" "dstdir/$j"
    done

For a different scheme, GNU's cp and mv can make numbered backups instead of overwriting -- see -b/--backup[=CONTROL] in the man pages.

find srcdir -name '*.mp3' -exec cp -v --backup=numbered {} dstdir/ \;
ephemient
+1  A: 

In python: to actually move the file, change debug=False

import os, re
from_dir="/from/dir"
to_dir = "/target/dir"
re_ext = "\.mp3"
debug = True

w = os.walk(from_dir)
n = w.next()
while n:
    d, arg, names = n
    names = filter(lambda fn: re.match(".*(%s)$"%re_ext, fn, re.I) , names)
    n = w.next()
    for fn in names:
        from_fn     = os.path.join(d,fn)
        target_fn   = os.path.join(to_dir, fn)
        file_exists = os.path.exists(target_fn)
        if not debug:
            if not file_exists:
                os.rename(from_fn, target_fn)
            else:
                print "DO NOT MOVE - FILE EXISTS ", from_fn
        else:
            print "MOVE ", from_fn, " TO " , target_fn
Agathe
A: 

Since you don't care how the duplicate files are named, utilize the 'backup' option on move:

find /path/to/mp3s -name *.mp3 -exec mv --backup=numbered {} /path/to/target/dir \;

Will get you:

  • song.mp3
  • song.mp3.~1~
  • song.mp3.~2~
Greg Bender