[bash] Renaming files in a folder to sequential numbers

I want to rename the files in a directory to sequential numbers. Based on creation date of the files.

For Example sadf.jpg to 0001.jpg, wrjr3.jpg to 0002.jpg and so on, the number of leading zeroes depending on the total amount of files (no need for extra zeroes if not needed).

This question is related to bash rename batch-rename

The answer is


Nowadays there is an option after you select multiple files for renaming (I have seen in thunar file manager).

  1. select multiple files
  2. check options
  3. select rename

A prompt comes with all files in that particular dir just check with the category section


Let us assume we have these files in a directory, listed in order of creation, the first being the oldest:

a.jpg
b.JPG
c.jpeg
d.tar.gz
e

then ls -1cr outputs exactly the list above. You can then use rename:

ls -1cr | xargs rename -n 's/^[^\.]*(\..*)?$/our $i; sprintf("%03d$1", $i++)/e'

which outputs

rename(a.jpg, 000.jpg)
rename(b.JPG, 001.JPG)
rename(c.jpeg, 002.jpeg)
rename(d.tar.gz, 003.tar.gz)
Use of uninitialized value $1 in concatenation (.) or string at (eval 4) line 1.
rename(e, 004)

The warning ”use of uninitialized value […]” is displayed for files without an extension; you can ignore it.

Remove -n from the rename command to actually apply the renaming.

This answer is inspired by Luke’s answer of April 2014. It ignores Gnutt’s requirement of setting the number of leading zeroes depending on the total amount of files.


Here is what worked for me.
I Have used rename command so that if any file contains spaces in name of it then , mv command dont get confused between spaces and actual file.

Here i replaced spaces , ' ' in a file name with '_' for all jpg files
#! /bin/bash
rename 'y/ /_/' *jpg         #replacing spaces with _
let x=0;
for i in *.jpg;do
    let x=(x+1)
    mv $i $x.jpg
done

This script will sort the files by creation date on Mac OS bash. I use it to mass rename videos. Just change the extension and the first part of the name.

ls -trU *.mp4| awk 'BEGIN{ a=0 }{ printf "mv %s lecture_%03d.mp4\n", $0, a++ }' | bash

I spent 3-4 hours developing this solution for an article on this: https://www.cloudsavvyit.com/8254/how-to-bulk-rename-files-to-numeric-file-names-in-linux/

if [ ! -r _e -a ! -r _c ]; then echo 'pdf' > _e; echo 1 > _c ;find . -name "*.$(cat _e)" -print0 | xargs -0 -t -I{} bash -c 'mv -n "{}" $(cat _c).$(cat _e);echo $[ $(cat _c) + 1 ] > _c'; rm -f _e _c; fi

This works for any type of filename (spaces, special chars) by using correct \0 escaping by both find and xargs, and you can set a start file naming offset by increasing echo 1 to any other number if you like.

Set extension at start (pdf in example here). It will also not overwrite any existing files.


Sorted by time, limited to jpg, leading zeroes and a basename (in case you likely want one):

ls -t *.jpg | cat -n |                                           \
while read n f; do mv "$f" "$(printf thumb_%04d.jpg $n)"; done

(all on one line, without the \)


To me this combination of answers worked perfectly:

ls -v | gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.jpg\n", $0, a++ }' | bash
  • ls -v helps with ordering 1 10 9 in correct: 1 9 10 order, avoiding filename extension problems with jpg JPG jpeg
  • gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.jpg\n", $0, a++ }' renumbers with 4 characters and leading zeros. By avoiding mv I do not accidentally try to overwrite anything that is there already by accidentally having the same number.
  • bash executes

Be aware of what @xhienne said, piping unknown content to bash is a security risk. But this was not the case for me as I was using my scanned photos.


A very simple bash one liner that keeps the original extensions, adds leading zeros, and also works in OSX:

num=0; for i in *; do mv "$i" "$(printf '%04d' $num).${i#*.}"; ((num++)); done

Simplified version of http://ubuntuforums.org/showthread.php?t=1355021


a=1

for i in *.jpg; do
 mv -- "$i" "$a.jpg"
 a=`expr $a + 1`
done

using Pero's solution on OSX required some modification. I used:

find . -name '*.jpg' \
| awk 'BEGIN{ a=0 }{ printf "mv \"%s\" %04d.jpg\n", $0, a++ }' \
| bash

note: the backslashes are there for line continuation

edit July 20, 2015: incorporated @klaustopher's feedback to quote the \"%s\" argument of the mv command in order to support filenames with spaces.


Follow command rename all files to sequence and also lowercase extension:

rename --counter-format 000001 --lower-case --keep-extension --expr='$_ = "$N" if @EXT' *

The majority of the other solutions will overwrite existing files already named as a number. This is particularly a problem if running the script, adding more files, and then running the script again.

This script renames existing numerical files first:

#!/usr/bin/perl

use strict;
use warnings;

use File::Temp qw/tempfile/;

my $dir = $ARGV[0]
    or die "Please specify directory as first argument";

opendir(my $dh, $dir) or die "can't opendir $dir: $!";

# First rename any files that are already numeric
while (my @files = grep { /^[0-9]+(\..*)?$/ } readdir($dh))
{
    for my $old (@files) {
        my $ext = $old =~ /(\.[^.]+)$/ ? $1 : '';
        my ($fh, $new) = tempfile(DIR => $dir, SUFFIX => $ext);
        close $fh;
        rename "$dir/$old", $new;
    }
}

rewinddir $dh;
my $i;
while (my $file = readdir($dh))
{
    next if $file =~ /\A\.\.?\z/;
    my $ext = $file =~ /(\.[^.]+)$/ ? $1 : '';
    rename "$dir/$file", sprintf("%s/%04d%s", $dir, ++$i, $ext); 
}

To work in all situations, put a \" for files that have space in the name

find . -name '*.jpg' | gawk 'BEGIN{ a=1 }{ printf "mv \"%s\" %04d.jpg\n", $0, a++ }' | bash

To renumber 6000, files in one folder you could use the 'Rename' option of the ACDsee program.

For defining a prefix use this format: ####"*"

Then set the start number and press Rename and the program will rename all 6000 files with sequential numbers.


ls -1tr | rename -vn 's/.*/our $i;if(!$i){$i=1;} sprintf("%04d.jpg", $i++)/e'

rename -vn - remove n for off test mode

{$i=1;} - control start number

"%04d.jpg" - control count zero 04 and set output extension .jpg


If your rename doesn't support -N, you can do something like this:

ls -1 --color=never -c | xargs rename -n 's/.*/our $i; sprintf("%04d.jpg", $i++)/e'

Edit To start with a given number, you can use the (somewhat ugly-looking) code below, just replace 123 with the number you want:

ls -1 --color=never  -c | xargs rename -n 's/.*/our $i; if(!$i) { $i=123; } sprintf("%04d.jpg", $i++)/e'

This lists files in order by creation time (newest first, add -r to ls to reverse sort), then sends this list of files to rename. Rename uses perl code in the regex to format and increment counter.

However, if you're dealing with JPEG images with EXIF information, I'd recommend exiftool

This is from the exiftool documentation, under "Renaming Examples"

   exiftool '-FileName<CreateDate' -d %Y%m%d_%H%M%S%%-c.%%e dir

   Rename all images in "dir" according to the "CreateDate" date and time, adding a copy number with leading '-' if the file already exists ("%-c"), and
   preserving the original file extension (%e).  Note the extra '%' necessary to escape the filename codes (%c and %e) in the date format string.

with "rename" command

rename -N 0001 -X 's/.*/$N/' *.jpg

or

rename -N 0001 's/.*/$N.jpg/' *.jpg

Again using Pero's solution with little modifying, because find will be traversing the directory tree in the order items are stored within the directory entries. This will (mostly) be consistent from run to run, on the same machine and will essentially be "file/directory creation order" if there have been no deletes.

However, in some case you need to get some logical order, say, by name, which is used in this example.

find -name '*.jpg' | sort -n | # find jpegs
gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.jpg\n", $0, a++ }' | # build mv command
bash # run that command 

Pero's answer got me here :)

I wanted to rename files relative to time as the image viewers did not display images in time order.

ls -tr *.jpg | # list jpegs relative to time
gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.jpg\n", $0, a++ }' | # build mv command
bash # run that command

Here a another solution with "rename" command:

find -name 'access.log.*.gz' | sort -Vr | rename 's/(\d+)/$1+1/ge'

Beauty in one line:

ls -v | cat -n | while read n f; do mv -n "$f" "$n.ext"; done 

You can change .ext with .png, .jpg, etc.


I like gauteh's solution for its simplicity, but it has an important drawback. When running on thousands of files, you can get "argument list too long" message (more on this), and second, the script can get really slow. In my case, running it on roughly 36.000 files, script moved approx. one item per second! I'm not really sure why this happens, but the rule I got from colleagues was "find is your friend".

find -name '*.jpg' | # find jpegs
gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.jpg\n", $0, a++ }' | # build mv command
bash # run that command

To count items and build command, gawk was used. Note the main difference, though. By default find searches for files in current directory and its subdirectories, so be sure to limit the search on current directory only, if necessary (use man find to see how).


On OSX, install the rename script from Homebrew:

brew install rename

Then you can do it really ridiculously easily:

rename -e 's/.*/$N.jpg/' *.jpg

Or to add a nice prefix:

rename -e 's/.*/photo-$N.jpg/' *.jpg

I had a similar issue and wrote a shell script for that reason. I've decided to post it regardless that many good answers were already posted because I think it can be helpful for someone. Feel free to improve it!

numerate

@Gnutt The behavior you want can be achieved by typing the following:

./numerate.sh -d <path to directory> -o modtime -L 4 -b <startnumber> -r

If the option -r is left out the reaming will be only simulated (Should be helpful for testing).

The otion L describes the length of the target number (which will be filled with leading zeros) it is also possible to add a prefix/suffix with the options -p <prefix> -s <suffix>.

In case somebody wants the files to be sorted numerically before they get numbered, just remove the -o modtime option.