Rename Multiple Filenames With Different Date Formats Using Bash And Regular Expressions (Regex) Captures/Substitutions

A common way of batch renaming filenames on the command-line, is to use the "mv" & "sed" commands with a "for loop", or using "find" with the exec option.
Here I'll show a couple of examples utilizing both options.

Here is a typical rename example converting uppercase "JPG" filename extensions to lowercase "jpeg" using "mv", "sed" and command substitution.

Create test folder and populate it with a couple of test files.

$ mkdir test_folder && cd test_folder/
$ touch {1..3}.JPG
$ ls -1
1.JPG
2.JPG
3.JPG

Batch rename the files with "JPG" filename extension to "jpeg" filename extension, using sed.

$ for i in *.JPG; do mv "$i" "$(echo "$i" | sed 's/^\(.*\)\.JPG$/\1\.jpeg/')"; done
$ ls -1
1.jpeg
2.jpeg
3.jpeg

For a more elegant and faster solution using Bash parameter expansion/substitution.

$ for i in *.jpeg; do mv "$i" "${i%.jpeg}.jpg"; done
$ ls -1
1.jpg
2.jpg
3.jpg

This works well for a single folder containing files we want to rename.
If we have several nested folders with files we want to rename, using the find command and its exec option is a good choice.

This time we'll add another step and move every file we rename into a folder called "jpeg_folder"
Let's clear out our test folder, create subfolders and populate these with test files.

$ rm *
$ mkdir -p Subfolder.{1..5}
$ ls -1
Subfolder.1
Subfolder.2
Subfolder.3
Subfolder.4
Subfolder.5

Populate the folders with unique filenames and create the "jpeg_folder"

$ COUNT=1 ; for i in $(ls -1) ; do touch ./$i/$COUNT.{1..5}.JPG && ((COUNT++));done
$ mkdir jpeg_folder

That gives us the the following test scenario to work with

$ ls -1R
Subfolder.1
Subfolder.2
Subfolder.3
Subfolder.4
Subfolder.5
jpeg_folder

./Subfolder.1:
1.1.JPG
1.2.JPG
1.3.JPG
1.4.JPG
1.5.JPG

./Subfolder.2:
2.1.JPG
2.2.JPG
2.3.JPG
2.4.JPG
2.5.JPG

./Subfolder.3:
3.1.JPG
3.2.JPG
3.3.JPG
3.4.JPG
3.5.JPG

./Subfolder.4:
4.1.JPG
4.2.JPG
4.3.JPG
4.4.JPG
4.5.JPG

./Subfolder.5:
5.1.JPG
5.2.JPG
5.3.JPG
5.4.JPG
5.5.JPG

./jpeg_folder:

Let's use the "find" command to recursively find all files with a ".JPG" file extension and rename them with a ".jpeg" file extension, while also moving them into the "jpeg_folder"

$ find * -name "*.JPG" -exec sh -c 'mv  "$0" "jpeg_folder/$(basename "${0%.JPG}.jpeg")"' {} \;
$ ls -1R
Subfolder.1
Subfolder.2
Subfolder.3
Subfolder.4
Subfolder.5
jpeg_folder

./Subfolder.1:

./Subfolder.2:

./Subfolder.3:

./Subfolder.4:

./Subfolder.5:

./jpeg_folder:
1.1.jpeg
1.2.jpeg
1.3.jpeg
1.4.jpeg
1.5.jpeg
2.1.jpeg
2.2.jpeg
2.3.jpeg
2.4.jpeg
2.5.jpeg
3.1.jpeg
3.2.jpeg
3.3.jpeg
3.4.jpeg
3.5.jpeg
4.1.jpeg
4.2.jpeg
4.3.jpeg
4.4.jpeg
4.5.jpeg
5.1.jpeg
5.2.jpeg
5.3.jpeg
5.4.jpeg
5.5.jpeg

As you can see from the previous example, the "find" command can be a very effective tool when paired with a bit Bash command substitution and parameter expansion/substitution.

Batch Renaming Files With Different Date Formats

That brings us to files containing different date formats in the filename.
We usually have two common formats, depending on which side of the Atlantic we're on.
Below are those, formatted with the "date" command
So for the "13th of March 2015":

# MDY date format (Month, Day, Year)
$ date +"%m-%d-%Y"
03-13-2015

# DMY date format (Day, Month, Year)
$ date +"%d-%m-%Y"
13-03-2015

Let's convert files with either of these two date formats to your choice of format.
Again let's clear our test folder and populate it with a few test files in which the MDY date format is included in the filename.

$ rm -rf *
$ touch somefilename-03-{13..15}-2015.txt  
$ ls -1
somefilename-03-13-2015.txt
somefilename-03-14-2015.txt
somefilename-03-15-2015.txt

Let's convert those files to DMY format using sed in a "for loop"

$ for i in *.txt; do mv "$i" "$(echo $i|sed -E 's/(somefilename)\-([0-9]{2})\-([0-9]{2})\-([0-9]{4})(\.txt)/\1-\3-\2-\4\5/')"; done
$ ls -1
somefilename-13-03-2015.txt
somefilename-14-03-2015.txt
somefilename-15-03-2015.txt

For a faster version, converting it back to MDY format without the use of command substitution, sed or "piping", using Bash regular expressions.

$ for i in *.txt; do if [[ ${i} =~ (somefilename)-([0-9]{2})-([0-9]{2})-([0-9]{4})(\.txt) ]] ; then mv "$i" "${BASH_REMATCH[1]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[4]}${BASH_REMATCH[5]}"; fi; done
$ ls -1
ls -1
somefilename-03-13-2015.txt
somefilename-03-14-2015.txt
somefilename-03-15-2015.txt

We can then convert back and forth between either format by running the same command again.

$ for i in *.txt; do if [[ ${i} =~ (somefilename)-([0-9]{2})-([0-9]{2})-([0-9]{4})(\.txt) ]] ; then mv "$i" "${BASH_REMATCH[1]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[4]}${BASH_REMATCH[5]}"; fi; done
$ ls -1
somefilename-13-03-2015.txt
somefilename-14-03-2015.txt
somefilename-15-03-2015.txt

Voila! Hope you enjoyed it!
Lars Bjaerris