29

I want to rename multiple files in the same directory using Bash scripting. Names of the files are as follows:

file2602201409853.p
file0901201437404.p  
file0901201438761.p  
file1003201410069.p  
file2602201410180.p

I want to rename to the following format:

file2503201409853.p
file2503201437404.p  
file2503201438761.p  
file2503201410069.p  
file2503201410180.p

I was reading about the rename command, and try to do it this way, but it does nothing, I think I have questions about the syntax. Then I read that you can make a loop using the mv command as follows:

for file in cmpsms*2014*.p; do
    mv "$file" "${file/cmpsms*2014*.p/cmpsms25032014*.p}"
done

But I can not rename the files. What am I doing wrong?

6 Answers6

48

You were right to consider rename first. The syntax is a little strange if you're not used to regexes but it's by far the quickest/shortest route once you know what you're doing:

rename 's/\d{4}/2503/' file*

That simply matches the first 4 numbers and swaps them for the ones you specified.

And a test harness (-vn means be verbose but don't do anything) using your filenames:

$ rename 's/\d{4}/2503/' file* -vn
file0901201437404.p renamed as file2503201437404.p
file0901201438761.p renamed as file2503201438761.p
file1003201410069.p renamed as file2503201410069.p
file2602201409853.p renamed as file2503201409853.p
file2602201410180.p renamed as file2503201410180.p
Oli
  • 16,068
  • 9
    Note that this is the Perl rename on Debian and derivatives (Ubuntu, Mint, …). On other Linux distributions, rename is a completely different file renaming tool (which Debian ships as rename.ul). – Gilles 'SO- stop being evil' Mar 26 '14 at 20:33
  • If your distributive lack of such tool, look at @Michael Campbell answer. I think every distributive replace default rename like debian did. – Astery Sep 18 '15 at 10:08
25

This should do the trick:

for f in file*; do mv $f ${f/${f:4:8}/25032014}; done

It replaces the string beween the 4th and the 12th character with "25032014".

chaos
  • 48,171
11

this is really @Eric's answer from above - but it's an elegant answer so I'm reposting it as a proper answer to draw more attention to it.

for f in *Huge*; do mv "$f" "${f/Huge/Monstrous}"; done
AdminBee
  • 22,803
Yehosef
  • 221
1
ptrn='file[0-9][0-9][0-9][0-9]2014[0-9][0-9][0-9][0-9][0-9].p'
path=/dir
( set -- "${path}/"${ptrn} ; for f ; do {
    echo "mv ${path}/${f} \
        ${path}/file25032014${f#2014}" 
} ; done )

This should do the trick. Note - I'm not in the habit of handing over mass mv commands - as written it's just an echo. You'll have to sort that out before it'll work.

mikeserv
  • 58,310
1

A simple native way to do it without looping, with directory traversal:

find -type f | xargs -I {} mv {} {}.txt

It will rename every file in place.

And below a working example with parallelization:

find -name "file*.p" | parallel 'f="{}" ; mv -- {} ${f:0:4}2503${f:8}'
dtrckd
  • 244
0

If you find you do this sort of thing a lot, look up "rename.pl". It's a perl script, but allows you to simply feed it perl code (like a s/// expression), and it will do a mass rename based on that expression or code.

There's a basic version here: http://stackoverflow.org/wiki/Rename.pl but there are other versions floating around the net. It's been around for ages and ages and ages.