Rename all ".JPG" files to ".jpg" under all subfolders...

Hi, Dear all:

One question ! :slight_smile:
I'm using bash under Ubuntu 9.10.

My question is not to rename all ".JPG" files to ".jpg" in a single folder, but to rename all ".JPG" files to ".jpg" in all subfolders.

To rename all ".JPG" to ".jpg" in a single folder,

for x in *.JPG; do mv "$x" "${x%.JPG}.jpg"; done

works fine for me.

But how to recursively enter every subfolder and rename all the files?
(Well, if possible, enter every subfolder's subfolder.. recursively?? )

In sum, given a folder, all files under this folder, and all its subfolder, and all the subsubfolders, should be finally renamed in one specific rule.

How to deal with it?

Thank you very much and looking forward to your reply.

Best Regards
JIA Pei

I guess this is far from a professional solution, but it seems to work:

$ du -a
0	./001.JPG
0	./002.JPG
0	./img1/a.JPG
0	./img1/b.JPG
2	./img1
0	./img2/1.JPG
0	./img2/2.JPG
0	./img2/pic10/pic1.JPG
0	./img2/pic10/pic2.JPG
2	./img2/pic10
4	./img2
8	.
$ 
$ du -a | cut -d '/' -f2- | grep JPG
001.JPG
002.JPG
img1/a.JPG
img1/b.JPG
img2/1.JPG
img2/2.JPG
img2/pic10/pic1.JPG
img2/pic10/pic2.JPG
$ 
$ for x in `du -a | cut -d '/' -f2- | grep JPG`; do mv "$x" "${x%.JPG}.jpg"; done
$ du -a
0	./002.jpg
0	./img1/b.jpg
0	./img1/a.jpg
2	./img1
0	./img2/2.jpg
0	./img2/pic10/pic2.jpg
0	./img2/pic10/pic1.jpg
2	./img2/pic10
0	./img2/1.jpg
4	./img2
0	./001.jpg
8	.
$ 

Another way:

find /your/folder -type f | sed 's/\(.*\)\.JPG/mv \1.JPG \1.jpg/' | sh

That approach comes with hefty filename limitations (which may or may not be of importance to the original poster, but I'll mention them for completeness):

  • Filenames cannot contain newlines (when reading find's output, it's impossible to distinguish between two filenames and one with an embedded newline. Unfixable.
  • Filenames cannot contain any of the other shell-special characters (space, tab, backslash, single-quote, double-quote, semicolon, ampersand, pipe, >, <, etc). Possibly fixable, by backslash escaping every such character, but it would be a brittle solution.

Unrelatedly, either find should be constrained to match only files ending in ".JPG" or the matching regular expression in sed should be anchored to the end of the line, with "JPG$". Otherwise, mid-filename ".JPG" sequences will match filenames not ending in ".JPG".

Regards,
Alister

---------- Post updated at 02:26 PM ---------- Previous update was at 01:52 PM ----------

If the solution must handle every allowable filename, I would suggest:

find . -type f -name \*.JPG -exec ./mvjpg.sh {} +

Where mvjpg.sh is:

#!/bin/sh

for f; do
    mv "$f" "${f%.JPG}.jpg"
done

Obviously, you'll need to adjust the path to the shell script in the -exec primary to match its actual location on your system, if it's not on your $PATH.

Regards,
Alister

Dear alister:

This works perfect for me !!!!

Thank you very much !!! Thank you.

Best Regards
JIA

Some shell parameter substitution might help.

 blar=foo.JPG
 echo ${blar/JPG/jpg}

will output:

foo.jpg

So that means you can do:

for JPG in `find /path -type f -name \*.JPG` ; do mv $JPG ${JPG/JPG/jpg} ; done

No need for a separate script.

you can even use a simple rename command as

find -name '*.JPG' -exec rename .JPG .jpg {} \;

There is if you care about handling filenames that have any IFS characters. Assuming that IFS has its default value, your code will choke on any filename that contains a space, tab, or newline.

Also, there is no way to fix that shortcoming. If you double-quote the command substitution in the for loop's list, it will always result in a list with a single item.

The shell substitution that the original poster's code uses works correctly and is part of the posix standard. Your subsitution is an extension and will mishandle filenames with an embedded ".JPG" sequence (though such a filename may be unlikely, the limitation should be mentioned).

$ f=image.JPG.JPG
$ echo ${f/JPG/jpg}
image.jpg.JPG

Regards,
Alister

---------- Post updated at 02:38 AM ---------- Previous update was at 02:34 AM ----------

That's a very nice approach, but alas rename is not a standardized utility and may not be present. If it is, your approach is most convenient.

By the way, you could speed it up by not having to invoke rename once per filename, using:

find -name '*.JPG' -exec rename .JPG .jpg {} +

Also, I get the impression, from reading the man page, that your solution may incorrectly rename files that contain an embedded .JPG before the end of the filename. It may be an uncommon scenario, but still, for completeness' sake, I mention it.

Regards,
Alister