6

During importing my music files from backup (thanks to default naming scheme of rhythmbox), I have messed up the file names. Now the looks like:

00 - American Pie.ogg.ogg.ogg.ogg
00 - American Pie.ogg.ogg.ogg.ogg.ogg
00 - another brick in the wall.ogg.ogg.ogg.ogg
00 - another brick in the wall.ogg.ogg.ogg.ogg.ogg
00 - candle_in_the_wind.ogg.ogg.ogg.ogg
00 - candle_in_the_wind.ogg.ogg.ogg.ogg.ogg

While the file should look like

American Pie.ogg
another brick in the wall.ogg
candle_in_the_wind.ogg

And I have (as from wc -l) 3096 such files. How can I restore it in batch mode? I have tried rename and mmv to work, as given in the ans of this question, but not working (problem with the rename syntax, and for mmv,there is collision).

Any help please?

BaRud
  • 1,639
  • 2
  • 17
  • 26

5 Answers5

7

Using perl-rename (which is one of the two tools that is commonly called rename; the other one uses a very different syntax and can't do this in one step):

rename -f 's/00 - ([^.]*).*/$1.ogg/' *.ogg

The -f or --force option makes rename overwrite any existing files.

The second part is a perl-style regular expression substitution. The basic syntax is s/replacethis/withthis/ The pattern to match -- 00 - ([^.]*).* -- will match all the *.ogg files with names like those in your question. 00 - -- obviously, that just matches the pattern at the beginning of each of the filenames. ([^.]*) is the meat of the regular expression. [^.] will match any single character that isn't a ., while * means 'any number of the previous thing', so [^.]* means 'any number of any characters that aren't .'. The parentheses mark out a capture group (more on that in a second). In regular expressions, . means 'any character' (if you want a literal dot on this side of the substitution you have to escape it, as in: \.), so the final .* means 'any number of any character'.

In the second part of the substitution command, $1 means 'the first capture group' -- that is, that which is contained within the first pair of parentheses (see? Told you I'd come back to it). The .ogg means a literal '.ogg' -- on this side of the substitution, you don't need to escape the dot.

So, roughly translated into English, 's/00 - ([^.]*).*/$1.ogg/' is telling rename to 'take "00 - ", followed by (any number of characters that aren't a dot), then any number of characters; and replace that with the characters contained within the brackets and ".ogg."'.

On some systems, perl-rename is called prename (when rename is taken by the aforementioned other program). On some systems it isn't available at all :(

For recursiveness, you can do one of the following:

shopt -s globstar ## assuming your shell is bash
rename 's/00 - ([^.]*).*/$1.ogg/' **/*.ogg

Or:

find . -name '*.ogg' -exec rename 's/00 - ([^.]*).*/$1.ogg/' {} +
evilsoup
  • 6,807
  • 3
  • 34
  • 40
4

This should work:

$ for f in *ogg; do 
   mv "$f" "$(printf "%s" "$f" | perl -lpe 's/^\d+ - (.*?).ogg.*/$1.ogg/')";
 done

To do this for all files, including those in subdirectories, either activate globstar if you're using bash:

$ shopt -s globstar
$ for f in **/*ogg; do 
   mv "$f" "$(printf "%s" "$f" | perl -lpe 's/^\d+ - (.*?).ogg.*/$1.ogg/')";
 done

Explanation:

The funky stuff is being handled by perl. It is a simple regular expression that removes all numbers (\d+) followed by - (that's a dash with space on either side) from the beginning of the input string, matches everything until the fist .ogg and replaces eveything after that with the matched string ($1) and .ogg.

This could be expanded to the more cumbersome but more readable:

for f in *ogg; do 
   newname=$(printf "%s" "$f" | perl -lpe 's/^\d+ - (.*?).ogg.*/$1.ogg/')";
   mv "$f" "$newname";
 done
terdon
  • 242,166
3

A Bourne-compatible shell can do this without Perl or sed (but its aesthetic appeal is questionable...)

for f in *.ogg
do
    newfile="${f#00 - }"          ## Strip the leading "00 - "
    newfile="${newfile%%.ogg*}"   ## Strip .ogg and everything after
    newfile="${newfile}.ogg"      ## Append .ogg
    mv "$f" "$newfile"            ## Do the rename
done

Note that is also possible to use ls -1 | while read f to create the loop, but this construct would break in the event it encountered a filename with a newline character. Using the shell's standard globbing behaviour guards against this problem.

D_Bye
  • 13,977
  • 3
  • 44
  • 31
  • Why not for f in *.ogg rather than ls -1 | while read f? Apart from that, this is a good answer, probably what I'd do if my system didn't have (perl-)rename. – evilsoup Jan 24 '14 at 12:02
  • Mainly force of habit. I don't think there's any difference in behaviour in this case. – D_Bye Jan 24 '14 at 12:06
  • I think there is a difference on those edge cases where you have newlines in filenames. There's also probably a performance difference from that pipe, but that's not worth worrying about in practice. – evilsoup Jan 24 '14 at 12:11
  • As I said - no difference in this case. But yes, ls -1 | while read f would break on really funky filenames. I'll modify the answer to use the safer for f in *.ogg – D_Bye Jan 24 '14 at 12:17
1

That's where Midnight Commander comes handy. Select the files with "+" or "Insert". Press F6 for rename. Set mask to *.ogg.ogg.ogg and the destination to * and press Enter. Sure, it's not automated, but it's still "in batch".

proski
  • 176
0
ls -1|while read f; do 
    newfile=$(echo $f|sed -e 's/\(\.ogg\)*$/.ogg/')
    mv "$f" "$newfile"
  done