Today was again the day where I needed to rename over 100 file names of an mp3-collection. And again it took me half an hour to make it work. That’s way I decided to write it down now, so I won’t have to think about it again.
The situation is very simple to explain.
My brother-in-law wanted his legally purchased mp3-collection renamed, because he renamed every single song title to something not very smart before. Now some of these mp3s refuses to be inserted into a k3b project list, in order to be recorded onto a DVD-R.
Here goes an example of five song titles:
001 - my first song - Interpreters name [here goes some crap].mp3 002 - my second song - One more Interpreters name [here goes some crap].mp3 003 - my third song - And again one Interpreters name [here goes some crap].mp3 004 - my fourth song - Here's another one [here goes some crap].mp3 005 - my fifth song - Singer [here goes some crap].mp3
Now you just want to have the crap in the song’s title removed. For example the first song:
001 – my first song – Interpreters name [here goes some crap].mp3
should actually look like this:
001 – my first song – Interpreters name.mp3
Well, this can be done in a one-liner, what I also actually did:
IFS=""; for I in $( ls ); do echo $I | awk '{ match($0,"here goes some crap"); print "mv substr($0,1,3)"* " """substr($0,1,RSTART-5)".mp3""}' ; done > rename.sh
Attention!
This example won’t work for you, unless you’ve adjusted the crap pattern accordingly to your crap. And it won’t work either, if your mp3 doesn’t start with three digits!
I’ve redirected the output into a file, called rename.sh
.
You should definitely look into this file and verify its content, afterwards you should make it executable (chmod 755 rename.sh
). And please backup your mp3-collection first! (If you read this Blog regularly, you know I don’t make use of exclamation marks often, so please backup your collection first)
One-liner explained:
Internal Field Seperator (
IFS
)
- If you don’t set this variable, all song titles will we truncated, because after every seperator sign, like a space, tab, or whatever a new line would be assumed.
The loop
- This is really nothing dramatic. A loop in a shell is easy. E.g.
for I in $(seq 5); do echo $I; done
The command, that is embraced between the ‘do’ and ‘done’, is executed. And in this example five times.
awk
- awk isn’t that easy anymore, but no rocket science either. We have a
match( )
, where we’re looking for the crap in question.- The position of the found crap is stored in the special variable
RSTART
- to cut out given pieces of crap from a certain position and for x charaters is done with the
substr( )
command - finally we print the result with the
print
command.
You’ ve definitely have to adjust the crap’s name of the example above, and maybe your crap has special characters also. Then you have to put backslashes before them.
In the case above I also avoid to print the full song title name with the crap in it, because the output wouldn’t work with special chars anymore. If you don’t have a leading 3 digit number, you won’t be able to move e.g. “001*
” to “001 - song.mp3
” anymore, and the script would look a bit more complicated. In such cases you should be good at shell scripting or use a more sophisticated tool for it.
Btw. If you’ve got such an absolutely unbearable crap in your name, where you even don’t have such keys on your keyboard, and cut&paste doesn’t work either, consider using inodes. The find command calls it ‘inum‘. So if you for example like to delete really evil crap from your hdd, you can always list your directories content with ls -li
. Here is a little sequence, which shows you how a file is created first, listed, and deleted again, without having used its name:
#touch crap #ls -li 483592 -rw-r--r-- 1 acme acme 0 2009-01-11 23:41 crap #find . -inum 483592 -exec rm {} ;
There’s probably a few hundred if not thousands of Linux rename scripts/tips out there.
Personally, I’m using something I picked out of a Perl book examples a long time ago, and called it “rename” not surprisingly. It does require you’re familiar with (perl) regular expressions; if you’re not, then this is probably not for you.
Your example would be done like this:
rename ‘s/[.*]//’ *.mp3
(with s/ … / … / being the substitute operation).
Here’s the perl code:
#!/usr/bin/perl
# Usage: rename perlexpr [files]
($op = shift) || die "Usage: rename perlexpr [filenames]n";
if (!@ARGV) {
@ARGV = ;
chop(@ARGV);
}
$filenum = 0;
for (@ARGV) {
$filenum++;
$was = $_;
eval $op;
die $@ if $@;
rename($was,$_) unless $was eq $_;
}
Oops, looks like the HTML ate part of the code.
Line 7 be like this (I hope this works now):
@ARGV = <STDIN>;
@Wizzu
Really cool!
Another question I’ve been asking myself for ages now is, how to do this DOS example in a bash shell:
move *.avi *.mpg
The bash solution for me atm is still:
for I in $(ls *.avi)
do
WITHOUTH_AVI=$(echo $I | tr -d ".avi$")
mv $I "$WITHOUT_AVI".mpg;
done
and in this example must not be SPACES in the filename.
Does anybody have another soltion?
Well, my rename script can do that with the s/// expression easily (‘s/.avi$/.mpg/’) but if that’s not what you want, then I suppose your shell commands work fine.
I should point you there’s a “basename” command which gives you the file’s name without the extension, so no need to do echo/tr trickery.
One web page I just googled shows it as:
for old in *.avi; do mv “$old” “`basename $old .avi`.mpg”; done
You can work also with filenames containing spaces if you put double quotes in proper places (I added them in the above).
@Wizzu
Cool!
The ‘s///’ trick is cool, and I’ve discovered it for me also. Actually I wrote a little article about it immediately, because I was so excited 😉 See here.
But your
for; do ... ;done
line was a quite a surprise, because I didn’t know the ‘basename NAME [SUFFIX]‘ trick. This is really cool, and is much cleaner this way!Thanks a lot!