Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Supposed I have five mp3 files:

01-trackfoo.mp3
02-trackbar.mp3
03-trackbaz.mp3
04-trackabc.mp3
05-trackxyz.mp3

Now I give a quick listen to the files (just mplayer on console will do the trick) and I find out that the order is wrong. 05 is actually 02 and the rest will have to be renamed. So first I will do a temp rename:

01-trackfoo.mp3

02-trackbar.mp3
03-trackbaz.mp3
04-trackabc.mp3
00-trackxyz.mp3

Now we need a "shift": 02 should become 03, 03 should become 04 and 04 should become 05. To minimize confusion, ex-05 (now 00) will just be mved later.

My approach was this: (perl rename, by Larry Wall, default here on Debian)

rename 's/0([2-4])([\s\S]+)/0($1+1)$2/' *

as well as (later, after some more RTFM'ing)

rename 's/0([2-4])([\s\S]+)/0($1+1)$2/e' *

None of them worked, especially because the /e[val] modifier does not accept anything else but evaluations, and will throw an error once you attempt to combine the evaluations with strings. The bash can do it just fine, e. g. foo$((1+6)) will be evaluated to foo7.

So how can I do this (one-liner preferred, not intending to write a whole standalone script just for this)?

share|improve this question

2 Answers 2

up vote 3 down vote accepted

You almost had it. You just need to make literal strings and string concatenation explicit within the /e-modified substitution, using quotes and the dot operator.

rename 's/0([2-4])([\s\S]+)/"0".($1+1).$2/e' *
share|improve this answer
    
It leaves the original 05 still un-renamed, but +1 for resolving the one-liner glitch. –  Peter.O Sep 3 '12 at 8:16
    
(minor note, rename is called prename in Ubuntu) ... If no names in the directory starts with $'\x01'2 then a preliminary renaming like this: prename 's/^05/\x012/' 05* .. then the above [2-4] rename .. then a final prename 's/^\x01/0/' $'\x01'2* will do the trick.. That makes 3 one-liners... For a fourth, and an initial check for the possible presence of a file starting with $'\x01', something like this bash snippet will trap it .. shopt -s nullglob; x=($'\x01'*); [[ -n $x ]] && { echo '\x01' is not suitable; exit; } –  Peter.O Sep 3 '12 at 9:13
    
YEEES! Thank you Alan, I didn't think of that dot operator, though I did know what to do in human-readable words: "tell /e not to touch the stuff outside the ( )" :) And thanks to Peter for the bash approach as well. Besides, if I had had any idea of how close PHP syntax is to Perl's, I would probably have found the solution by trial-and error. Because that dot IS the JavaScript '+' counterpart in PHP for string concatenation. –  syntaxerror Sep 3 '12 at 10:37

one-liner preferred, not intending to write a whole standalone script just for this

When things get so complicated, I don't see any reason not to write a script. You're never going to remember how to do this from one run to the next, so you're going to end up either reinventing it each time, or wrapping it up in a script anyway.

For a small thing like this, I generally start trying to solve it in Bash:

#!/bin/bash
if [ -z "$1" ] ; then echo Need arguments. ; exit 1 ; fi

typeset -i i=1
for f in "$*"
do
    tailbits=`echo "$f" | sed -e 's/^[0-9]+//'`
    mv "$f" sertmp-`printf %02d $i`"$tailbits"
    i=i+1
done

for f in "sertmp-*"
do
    mv "$f" `echo "$f" | sed -e s/^sertmp-//` 
done

Basically, this script strips off any leading digits, then puts a zero-padded increasing serial number on the front, with the files numbered according to the order you pass them to the script.

It does this in two stages, with sertmp- prefixes for the first pass to avoid any risk of name collisions. If you call this script mp3-renamer and call it like:

$ mp3-renamer 01-foo.mp3 03-bar.mp3 04-qux.mp3

you run into a trivial collision on the first rename if you don't use 2 passes to do the renaming. (01-foo.mp3 -> 01-foo.mp3.)

If you call it like this, though:

$ mp3-renamer 02-foo.mp3 01-foo.mp3

you accidentally erase 01-foo.mp3 in the first rename with a 1-pass rename.

If the problem gets more complicated, I'd rewrite it in Perl. At that point, you could then use a hash to hold the old->new name mapping, and use a bit of clever code to work out the proper order to do the renamings in order to avoid the need for 2 passes.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.