I have a some jpg files in a certain folder:


I want to rename the files in reverse order, so that the name of the first file will be exchange with the last file, second one with the second last and so on.

In other words:

hg_test_spr.jpg --> DO NOT RENAME THIS
hg_test00001.jpg --> hg_test01200.jpg
hg_test00002.jpg --> hg_test01199.jpg
hg_test00003.jpg --> hg_test01198.jpg
hg_test00004.jpg --> hg_test01197.jpg
hg_test00005.jpg --> hg_test01196.jpg
hg_test00006.jpg --> hg_test01195.jpg
hg_test01199.jpg --> hg_test00002.jpg
hg_test01200.jpg --> hg_test00001.jpg

I can get the list of files in reverse order in this way:

ls *.bmp | tac | tee reverse_order.txt

Or perhaps better would be:

ls *hg_test0*.bmp | tac | tee reverse_order.txt

And then maybe something like this:

mkdir renamed
for file in *hg_test0*.bmp; do read line;  cp -v "${file}" "renamed/${line}";  done < reverse_order.txt

What is the best way to do this?

  • 1
    Are files upto 1000 have four numerical digits and from higher have five?
    – Inian
    Jul 26, 2018 at 11:49
  • If that is the case lexicographically, hg_test001000 would come after hg_test0010 which would be incorrect
    – Inian
    Jul 26, 2018 at 11:57
  • Sorry, that was a mistake in my post. My original files all have the same number of digits. I will fix this.
    – benett
    Jul 26, 2018 at 12:00

2 Answers 2


Going by your numerical ordering of files with 5 digits representing it, you could just use the glob features of the shell to rename as you would want to

shopt -s nullglob

for file in hg_test[0-9]*; do 
     echo "$file" "hg_test$(printf "%05d" "$count").jpg"

This will basically echo the actual file name with the replaced file name. Once you find the names are intact as expected, remove the echo and use the mv command.


Assuming that hg_*[0-9].jpg expands to all the files that you'd like to rename, in the correct "forward" order, and that you'd like to write the renamed files into the subdirectory renamed.

Using bash:

mkdir renamed || exit 1    # fail if this directory exists
names=( hg_*[0-9].jpg )

for (( i=0; i < "${#names[@]}"; ++i )); do
    cp "${names[i]}" renamed/"${names[-(i+1)]}"

This first generates a list of filenames that are kept in the names array. The loop goes through this generated list of names and picks a new name for each file at an offset from the end of the list. No existing file will be modified and copies with new names will be written to the renamed folder.

  • Why so complicated? This does exactly what the sample code in my original post does, but the code is unnecessarily complicated and long.
    – benett
    Jul 26, 2018 at 12:07
  • 2
    @benett If your code does what you want it to do, then I don't quite see the point of your question. My code just uses an array instead of a temporary file. It also supports any weird filename that you may want to throw at it (including names with embedded newlines, even if your files may not be that strange).
    – Kusalananda
    Jul 26, 2018 at 12:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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