views:

461

answers:

10

I have a lot of files I'm trying to rename, I tried to make a regular expression to match them, but even that I got stuck on the files are named like:

File Name 01

File Name 100

File Name 02

File Name 03

etc, I would like to add a "0" (zero), behind any of file that are less than 100, like this:

File Name 001

File Name 100

File Name 002

File Name 003

The closest I got to so much as matching them was using this find -type d | sort -r | grep ' [1-9][0-9]$' however I could not figure out how to replace them. Thanks in advance for any help you can offer me. Im on CentOS if that is of any help, all this is being done via SSH.

A: 

I think mmv is your friend here.

Ken
+9  A: 
find . -type d -print0 | xargs -0 rename 's/(\d+)/sprintf "%03d", $1/e'

or something like that, provided

  1. You have GNU find and GNU xargs (for -print0 and -0)
  2. You have the 'rename' utility that comes with perl
  3. There's only one group of digits in the filename. If there's more than one, then you need to do something with the regex to make it only match the number you want to reformat.
hobbs
That's close to something I was going to suggest, using sed instead of xargs, so I'll +1 it.
shoover
I knew xargs was the way to go, I just couldn't write the exact command.
Martinho Fernandes
+1 For listing dependencies (it's possibly the rename utility you are referring to is one yum or apt-get away).
ChristopheD
I've always been pissed that the cygwin perl distro doesn't include rename.
rmeador
+2  A: 

Run two commands, in this order:

$ rename 's/File Name (\d)$/File Name 0$1/' *
$ rename 's/File Name (\d\d)$/File Name 0$1/' *

First one renames everything less than 10 and prepends a zero. The second one renames everything less than 100 and prepends a zero. The result should be three digits for all filenames.

ire_and_curses
In what environment are those valid commands?
Rob Kennedy
`rename` is a utility which comes with the standard perl distribution. I ran these under Linux Bash.
ire_and_curses
[The $ is supposed to represent a command prompt.]
ire_and_curses
A: 

you could do something using perl or ruby.

put all this files in the same directory

dirlisting = DIR.entries('.')

dirListing.each do |file| 
 num = file.match(/\d+$/).to_i
 if num < 100
   find the position where start the number, with index and inject the 0 there.
 end
end
VP
You should probably mention that this code is ruby and not perl
Martinho Fernandes
if you read my sentence you would see "perl OR ruby"
VP
And that's why I said you should disambiguate the fact that the code is ruby.
Martinho Fernandes
+4  A: 

Is this a one-time thing? If so, I'm going to suggest something that might seem to be a cop out by many programmers here:

Pipe the output of your command (find -type d | sort -r | grep ' [1-9][0-9]$') to a file and use an editor along with some global search/replace magic to create a script that does the renames.

Then throw away the script.

There's little fuss and little chance that you'll end up shooting yourself in the foot by having some attempt at a clever (but inadequately debugged) one-liner go off into the weeds on your files.

Michael Burr
++! Sometimes it is better to use a kludge you understand than to learn "deep magic".
daotoad
+1 I think this sort of thing is often the most pragmatic solution for one off requirements
Brian Agnew
It may be a cop-out, but it's a safe cop-out...kudos.
Mark Krenitsky
A possible global search and replace magic in vim for this is `:s/^\(.*\)\(\d\d\)$/mv \1 \10\2/`
Martinho Fernandes
In Emacs you can open a directory listing, press C-x C-q, edit the filenames in the buffer (e.g. with search and replace, or any other command), and upon saving Emacs will rename the files.
Phil
A: 

Does File Name represent a random string or are they actually called File Name (or some other fixed length string), if the text part is fixed length then this is easy.

Paul Creasey
This should be a comment, not an answer.
Martinho Fernandes
Paul, while I found the answer, Im open to seeing different ways on how to do this for my own benefit, all the files were named "File Name ##" the only difference was the 2 or 3 numbers at the end.
linux_rookie
My rep, was too low at time Martinho
Paul Creasey
+9  A: 
perl -e 'foreach $f (glob("File\\ Name*")) { $nf = $f; $nf =~ s/(\d+)$/sprintf("%03d",$1)/e; print `mv \"$f\" \"$nf\"`;}'

A bit overkill maybe, but it does what is asked.

Jeff B
Worked like a charm!
linux_rookie
@Jeff, Nice answer and glad to find you here again :)
Mike
A: 
use strict;
use File::Copy;

my @files = glob 'File*Name*';

foreach my $filename (@files) {
    if ($filename =~ m`^.*File.*Name.*?(\d+)`) {
        my $number = $1;
        next if ($number > 99);
        rename $filename, sprintf("FileName%03d",$number);
    }
}
chris d
That renames `File Name 100` to `File Name 0100` which is not intended.
Martinho Fernandes
You were most certainly right. Added some code to make it so that won't happen.
chris d
+1  A: 

In my debian it works well with rename, tested with 300 files.

 perl -e 'map `touch door$_.txt`, 1..300;'
 rename 's/(\d+)\.txt/sprintf("%03d.txt", $1)/e' *.txt
Pavel Reich
A: 

if your remote has bash shell

for i in File*; 
do 
    case "${i##* }" in  [0-9][0-9] ) 
      echo  mv "$i" "${i% *} $(printf "%03d" ${i##* })" ;; 
    esac; 
done

remove "echo" to do actual renaming