This question already has an answer here:

I have about a thousand files that all look something like this:


The first eight are the date, the last 6 are consecutive numbers, but the difference between those numbers isn't the same between the files. I want the last six numbers to be consecutive and always with the same step. For example:


I checked several questions on this site using mmv and others like https://www.ostechnix.com/how-to-rename-multiple-files-at-once-in-linux/ but none of them can explain to me how to have consecutive numbers in my name.

To differentiate this from the question Batch rename files to a sequential numbering, the sequential ordering must be based on the last six digits, ignoring the embedded date in the first eight characters completely..

marked as duplicate by steeldriver, Community Jan 31 at 16:13

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • Mmm. I've re-read your question (after posting my answer). Is the ordering supposed to be based entirely on the last six digits, regardless of the date embedded in the first eight characters? – roaima Jan 31 at 16:21
  • @steeldriver quite possibly not a duplicate (see my previous comment) – roaima Jan 31 at 16:23
  • Tricky, since the OP seems to have accepted the suggested duplicate (though, maybe being new wasn't sure what that meant)? Jellyse, please see roaima's question/comment above. – Jeff Schaller Jan 31 at 20:42
  • Yes the question 'Batch rename files to a sequential numbering ' seems to answer my question but I totally forgot all the dates are different --' So it's not a duplicate after all... – Jellyse Feb 1 at 9:01

If you're got the perl-based rename tool installed, which is sometimes known as prename, you can do this in one line, like this

rename -n 's/^(.{8})(.{6})\.(.*)/sprintf "%s%06d.%s", $1, ++$a, $3/e' *.nc

As written it will only tell you what it would have done. When you're happy, remove the -n to run silently, or replace it with -v to see what's happening.

In case you're curious, this replaces the three (..) sections (where the .{n} represents n characters, .* represents anything, and the brackets create a grouping) with the result of a formatted print comprising the first group and an incrementing six digit number. (The second grouping isn't used.) The third grouping carries across the file extension name.

I should note that it will refuse to overwrite existing files.

Sample output

20091208170014.nc renamed as 20091208000001.nc
20091208171946.nc renamed as 20091208000002.nc
20091211150704.nc renamed as 20091211000003.nc
20091211152610.nc renamed as 20091211000004.nc
20091214131328.nc renamed as 20091214000005.nc
20091217111953.nc renamed as 20091217000006.nc
20091220092643.nc renamed as 20091220000007.nc
20091223073308.nc renamed as 20091223000008.nc
20091226053932.nc renamed as 20091226000009.nc
20091229034557.nc renamed as 20091229000010.nc

You appear to want the ordered to be based on the last six digits of the filename, ignoring the embedded date completely. There are a couple of options here.

  1. If you have few enough files that the shell can expand the list completely, is to sort the files:

    rename -n '..as above..' $(ls -d *.nc | sort -k1.9,1.14n)
  2. Perform a transform so that the sorting key is temporarily placed at the front of the filename, and then renamed, and then replaced where it belongs:

    # Swap the first eight and second six groups around
    rename -n 's/^(.{8})(.{6})\.(.*)/$2$1.$3/' *.nc
    # Apply the transform with the shell sorting by original sequence
    rename -n 's/^(.{6})(.{8})\.(.*)/sprintf "%06d%s.%s", ++$a, $2, $3/e' *.nc
    # Swap back the first six and second eight groups
    rename -n 's/^(.{6})(.{8})\.(.*)/$2$1.$3/' *.nc

As before, remove the -n to run silently or replace it with -v to see what actually happened.

  • neat trick, keeping the $a state as it's one command! – Jeff Schaller Jan 31 at 16:10
  • @roaima you mean for the numbers to start recounting every day? That would be interesting as well – Jellyse Feb 1 at 9:38

In bash, and with no collision-checking:

for file in [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].nc
  mv -- "$file" "${file:0:8}"$(printf "%06d" "$index").nc

The wildcard [0-9]... picks up filenames that have 8+6 (14) digits followed by .nc. It loops through all those files and renames them. The destination filename is generated in three parts:

  1. "${file:0:8}" -- the first 8 characters of the existing filename (the date)
  2. $(printf "%06d" "$index") -- a 6-digit zero-padded index
  3. .nc -- the existing extension

When I run an "echo" version of the above loop on your sample files, I get:

mv -- 20091208170014.nc 20091208000001.nc
mv -- 20091208171946.nc 20091208000002.nc
mv -- 20091211150704.nc 20091211000003.nc
mv -- 20091211152610.nc 20091211000004.nc
mv -- 20091214131328.nc 20091214000005.nc
mv -- 20091217111953.nc 20091217000006.nc
mv -- 20091220092643.nc 20091220000007.nc
mv -- 20091223073308.nc 20091223000008.nc
mv -- 20091226053932.nc 20091226000009.nc
mv -- 20091229034557.nc 20091229000010.nc

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