Recursively rename files to their parent folder’s name

If you own a business and sell online, you will have had to – at some point or another – name a whole bunch of images in a specific way to stay organised. If you sell on behalf of brands, naming your images is even more important.

Problem: Lets say you had your images sorted in a bunch of folders. Each folder had the name of the brand with the brand’s images in the folder. To make it more complicated, a brand could have a sub-brand, and therefore a subfolder with that sub-brand’s images.

This looks like this in practice:

I was surprised at how difficult this was to accomplish. After scouring stack overflow for solutions that do something close, I came up with the solution presented here.

We will be using Python’s os library. First lets create a function that takes 2 parameters:

  1. The top-most folder that contains all the files and subfolders.
  2. A number indicating how many folders deep do we want to go

This look like this:

def renameFiles(path,  depth=99):

We will be calling this function recursively (over and over) until we are as deep as we want to go. For every folder deep we go, we want to reduce the depth by one. If we hit 0, then we know to go back up the folder tree. Lets continue building our code:

def renameFiles(path,  depth=99):
  if depth < 0: return

Now for every file we hit, we want to test for a few things:

  1. That the path to that file is not a shortcut or symbolic link
  2. That the path to the file is a real folder and exists
  3. If the file we look at is a real folder, we go one level deep

To do this we add the following code:

 
def renameFiles(path,  depth=99):
    # Once we hit depth, return
    if depth < 0: return
 
    # Make sure that a path was supplied and it is not a symbolic link
    if os.path.isdir(path) and not os.path.islink(path):
     # We will use a counter to append to the end of the file name
     ind = 1
 
     # Loop through each file in the start directory and create a fullpath
     for file in os.listdir(path):
       fullpath = path + os.path.sep + file
 
       # Again we don't want to follow symbolic links
       if not os.path.islink(fullpath):
 
         # If it is a directory, recursively call this function 
         # giving that path and reducing the depth.
         if os.path.isdir(fullpath):
           renameFiles(fullpath, depth - 1)

We are now ready to process a file that we find in a folder. We want to first build a new name for the file then test for a few things before renaming the file.

  1. That we are only changing image files
  2. We are not overwriting an existing file
  3. That we are in the correct folder

Once we are sure that these tests pass, we can rename the file and complete the function. Your function is then complete:

 
import os
 
'''
 
 Function: renameFiles
 Parameters: 
    @path: The path to the folder you want to traverse
    @depth: How deep you want to traverse the folder. Defaults to 99 levels. 
 
'''
 
def renameFiles(path,  depth=99):
    # Once we hit depth, return
    if depth < 0: return
 
    # Make sure that a path was supplied and it is not a symbolic link
    if os.path.isdir(path) and not os.path.islink(path):
     # We will use a counter to append to the end of the file name
     ind = 1
 
     # Loop through each file in the start directory and create a fullpath
     for file in os.listdir(path):
       fullpath = path + os.path.sep + file
 
       # Again we don't want to follow symbolic links
       if not os.path.islink(fullpath):
 
         # If it is a directory, recursively call this function 
         # giving that path and reducing the depth.
         if os.path.isdir(fullpath):
           renameFiles(fullpath, depth - 1)
         else:
           # Find the extension (if available) and rebuild file name 
           # using the directory, new base filename, index and the old extension.
           extension = os.path.splitext(fullpath)[1]
 
           # We are only interested in changing names of images. 
           # So if there is a non-image file in there, we want to ignore it
           if extension in ('.jpg','.jpeg','.png','.gif'):
 
             # We want to make sure that we change the directory we are in
             # If you do not do this, you will not get to the subdirectory names
             os.chdir(path)
 
             # Lets get the full path of the files in question
             dir_path =  os.path.basename(os.path.dirname(os.path.realpath(file)))
 
             # We then define the name of the new path in order
             # The full path, then a dash, then 2 digits, then the extension
             newpath = os.path.dirname(fullpath) + os.path.sep   + dir_path \
             + ' - '+"{0:0=2d}".format(ind) + extension
 
             # If a file exists in the new path we defined, we probably do not want
             # to over write it. So we will redefine the new path until we get a unique name
             while os.path.exists(newpath):
                ind +=1
                newpath = os.path.dirname(fullpath) + os.path.sep   + dir_path \
                + ' - '+"{0:0=2d}".format(ind) + extension
 
             # We rename the file and increment the sequence by one. 
             os.rename(fullpath, newpath)
             ind += 1
    return

The last part is to add a line outside the function that executes it in the directory where the python file lives:

renameFiles(os.getcwd())

With this your code is complete. You can copy the code at the bottom of this post.

How do you use this?

Once you have the file saved. Drop it in the folder of interest. In the image below, we have the file rename.py in the top most folder.

Next, you fire up terminal and type in “cd” followed by the folder where you saved your python script. Then type in “python” followed by the name of the python file you saved. In our case it is rename.py:

The results look like this:

The entire code

 
import os
 
'''
 
 Function: renameFiles
 Parameters: 
    @path: The path to the folder you want to traverse
    @depth: How deep you want to traverse the folder. Defaults to 99 levels. 
 
'''
 
def renameFiles(path,  depth=99):
    # Once we hit depth, return
    if depth < 0: return
 
    # Make sure that a path was supplied and it is not a symbolic link
    if os.path.isdir(path) and not os.path.islink(path):
     # We will use a counter to append to the end of the file name
     ind = 1
 
     # Loop through each file in the start directory and create a fullpath
     for file in os.listdir(path):
       fullpath = path + os.path.sep + file
 
       # Again we don't want to follow symbolic links
       if not os.path.islink(fullpath):
 
         # If it is a directory, recursively call this function 
         # giving that path and reducing the depth.
         if os.path.isdir(fullpath):
           renameFiles(fullpath, depth - 1)
         else:
           # Find the extension (if available) and rebuild file name 
           # using the directory, new base filename, index and the old extension.
           extension = os.path.splitext(fullpath)[1]
 
           # We are only interested in changing names of images. 
           # So if there is a non-image file in there, we want to ignore it
           if extension in ('.jpg','.jpeg','.png','.gif'):
 
             # We want to make sure that we change the directory we are in
             # If you do not do this, you will not get to the subdirectory names
             os.chdir(path)
 
             # Lets get the full path of the files in question
             dir_path =  os.path.basename(os.path.dirname(os.path.realpath(file)))
 
             # We then define the name of the new path in order
             # The full path, then a dash, then 2 digits, then the extension
             newpath = os.path.dirname(fullpath) + os.path.sep   + dir_path \
             + ' - '+"{0:0=2d}".format(ind) + extension
 
             # If a file exists in the new path we defined, we probably do not want
             # to over write it. So we will redefine the new path until we get a unique name
             while os.path.exists(newpath):
                ind +=1
                newpath = os.path.dirname(fullpath) + os.path.sep   + dir_path \
                + ' - '+"{0:0=2d}".format(ind) + extension
 
             # We rename the file and increment the sequence by one. 
             os.rename(fullpath, newpath)
             ind += 1
    return
 
renameFiles(os.getcwd())
Source: http://www.coderslexicon.com/batch-renaming-of-files-using-recursion-in-python/

Leave a Reply

Your email address will not be published. Required fields are marked *