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?

Peter Mortensen
  • 1,029
  • 1
  • 8
  • 10
franvergara66
  • 1,119
  • 3
  • 10
  • 10
  • possible duplicate of [How can I rename a lot of files using a regex?](http://unix.stackexchange.com/questions/24107/how-can-i-rename-a-lot-of-files-using-a-regex) – Kiwy Mar 26 '14 at 14:09
  • 2
    You can't have a pattern expression on the right hand side of a parameter expansion replacement expression. What did you expect to be where that `*` is? – kojiro Mar 26 '14 at 14:22
  • @Kiwy That question is about removing a portion of the file name. Ergo, highly doubtfully duplicate. – user Mar 26 '14 at 14:41
  • 2
    Next time, please describe the file name changes you want to make (I had to look at this for a fair while before seeing the differences and similarities). Examples are good, but a description *as well as* examples is even better. – user Mar 26 '14 at 14:43

6 Answers6

47

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
  • 15,808
  • 10
  • 42
  • 51
  • 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
  • 47,463
  • 11
  • 118
  • 144
  • this `for f in file*; do mv $f ${f/${f:4:4}/2503}; done` also work. – Avinash Raj Mar 26 '14 at 16:36
  • That's nice. If you just want to do a regular word for word replacement, it works like sed. e.g. `for f in *Huge*; do mv $f ${f/Huge/Monstrous}; done` – Eric Feb 06 '15 at 01:40
  • 1
    @Eric - I posted your answer as an alternative because I think it's more clear and elegant. If you also want to post it as an answer - I'll delete mine. Thanks! – Yehosef Nov 25 '15 at 15:23
  • Why the [missing double quotes](http://unix.stackexchange.com/q/131766/135943)? – Wildcard Dec 07 '16 at 01:39
  • It gives me an error: "mv: target 'FILE_NAME' is not a directory" – Aubrey May 02 '17 at 13:32
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
  • 21,637
  • 21
  • 47
  • 71
Yehosef
  • 221
  • 2
  • 5
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
  • 57,448
  • 9
  • 113
  • 229
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
  • 2
  • 10
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.