Renaming multiple files with complex numbered names

I have 365 files named

MOD11A1.A2010001.h24v06.006.2016025150444.hdf
MOD11A1.A2010002.h24v06.006.2016025151748.hdf
MOD11A1.A2010003.h24v06.006.2016025163706.hdf

and I want to rename these files as:

modis1.hdf
modis2.hdf
modis3.hdf

I am using the mv command separately for each file to rename them.

How do I achieve this in one go?


Since you only care about the A2010... bit, using rename in the directory with the files:

rename -n 's/.*A2010*(\d+).*/modis$1.hdf/' *

Example:

$ rename -n 's/.*A2010*(\d+).*/modis$1.hdf/' *
rename(> MOD11A1.A2010001.h24v06.006.2016025150444.hdf, modis1.hdf)
rename(> MOD11A1.A2010002.h24v06.006.2016025151748.hdf, modis2.hdf)
rename(> MOD11A1.A2010003.h24v06.006.2016025163706.hdf, modis3.hdf)
rename(> MOD11A1.A2010365.h24v06.006.2016025150444.hdf, modis365.hdf)

Run without -n to actually rename the files.


The following way uses bash parameter expansion, but it does rely on your filenames being in exactly the format you've stated as it relies on the placement of the numeric string to be used. This isn't necessarily the most robust method. You must also be in the directory containing the files.

for f in MOD*.hdf; do
  num="${f:13:3}"
  mv "$f" modis"$((10#$num))".hdf
done

Or as a one-liner:

for f in MOD*.hdf; do num="${f:13:3}"; mv "$f" modis"$((10#$num))".hdf ; done

This will move the files in the manner which you've asked for, meaning that MOD11A1.A2010001.h24v06.006.2016025150444.hdf will become modis1.hdf, MOD11A1.A2010002.h24v06.006.2016025151748.hdf becomes modis2.hdf etc.


You could use the rename command like this:

$ rename -n 's/MOD11A1\.A2010(...)\..*/modis$1.hdf/' MOD*.hdf
rename(MOD11A1.A2010001.h24v06.006.2016025150444.hdf, modis001.hdf)
rename(MOD11A1.A2010002.h24v06.006.2016025151748.hdf, modis002.hdf)
rename(MOD11A1.A2010003.h24v06.006.2016025163706.hdf, modis003.hdf)

After testing, remove -n to actually rename files.

Notes

  • s/old/new replace old with new
  • \. literal .
  • ... three characters that could be anything
  • (some chars) save some chars to reference later with $1
  • We could use a simple regex to match the first part of the filename in the rename command, as muru's answer does. Sometimes you want to use a more explicit expression to distinguish between files that you do or don't want to act on, or to be sure that your regex won't suck up too many characters. There's usually a lot of flexibility in solving this kind of problem.

Note that this doesn't give you exactly what you asked for; it maintains the fixed width of the numbers. I have done this because it's a good idea to use fixed-width numbers in filenames (otherwise they will be sorted in a confusing way, with 10 before 2, for example) (and because I haven't figured out how to use rename to create the names you actually asked for... if I do, I will update my answer muru's answer does that...).

Note that Perl rename is not included in the default 17.10 installation, so if you are running 17.10 (or for future readers, perhaps a later version), start by running

sudo apt install rename