2
\$\begingroup\$

Let's say I have this folder tree:

├──FolderParent   
  ├── FolderA   
      ├── FolderAA_IMG             -- should be renamed
          ├── file_IMG_AA1.txt     -- should NOT be renamed
          ├── file_VID_AA1.txt     -- should NOT be renamed
      ├── file_IMG_A1.txt          -- should be renamed
      ├── file_PANO_A1.txt         -- should be renamed
  ├── FolderB   
      ├── file_IMG_B1.txt          -- should be renamed
      ├── file_PANO_B1.txt         -- should be renamed

As you can see, only the files/folders in the first children folder should be renamed, not the other ones. I follow this base code and then I added a second loop above but I wonder if the double for loop is the right way to go.

import os

# custom var
path= r"C:\Users\user\FolderParent"

# other cvar
number_modified_files= 0

# get all path of subfolder
all_subfolders = [f.path for f in os.scandir(path) if f.is_dir() ] 
print(all_subfolders)

for folder in all_subfolders: 
    # set the path to the folder: otherwise the rename file won't work 
    os.chdir(folder)
    #won't rename files in subfolder but will rename folder in the path 
    for filename in os.listdir(folder):
        print("new file:", filename)
        if "IMG_" in filename:
            os.rename(filename, filename.replace('IMG_', ''))
            number_modified_files +=1
        elif "PANO_" in filename:
            os.rename(filename, filename.replace('PANO_', ''))
            number_modified_files +=1
        elif "VID_" in filename:
            os.rename(filename, filename.replace('VID_', ''))   
            number_modified_files +=1


print(f"End : {number_modified_file} files renamed")
\$\endgroup\$
3
\$\begingroup\$

You should rethink your solution in terms of regexes:

#!/usr/bin/env python3

import re
from os import scandir, rename, path


def rename_children(parent):
    n_renamed = 0
    re_fname = re.compile('(IMG|PANO|VID)_')

    for child_dir in scandir(parent):
        if child_dir.is_dir():
            for child in scandir(child_dir):
                renamed = re_fname.sub('', child.name)
                if renamed != child.name:
                    new_path = path.join(child_dir.path, renamed)
                    print(f'Renaming {child.path} to {new_path}')
                    rename(child.path, new_path)
                    n_renamed += 1
    print(f'{n_renamed} files renamed')

Note the following changes:

  • Only one if to check whether the regex matches
  • Use scandir instead of listdir
  • Do not call chdir; there's no point
  • Don't call replace; the pattern check and the replacement operation can be combined by using sub
  • Don't store a list of all_subfolders; simply iterate over the results
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Use regex to replace pattern in filename

The structure of these if else all similar, you can use re to simplify it.

if "IMG_" in filename:
    os.rename(filename, filename.replace('IMG_', ''))
    number_modified_files +=1
elif "PANO_" in filename:
    os.rename(filename, filename.replace('PANO_', ''))
    number_modified_files +=1
elif "VID_" in filename:
    os.rename(filename, filename.replace('VID_', ''))   
    number_modified_files +=1

So you are looking for IMG_, PANO_ and VID_ in filename and try to replace it delete this part.

Instead of using os.rename multiply times, we can use re.sub(pattern, repl, string, count=0, flags=0) to do this.

It will Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl.

pattern = 'IMG_|PANO_|VID_'
renamed_filename = re.sub(pattern, '', filename)

The pattern meaning match one in three. I am not sure if your are familiar with regex, here is the doc.

And if the renamed_filename not equal filename it is modified, so whole part will be

pattern = 'IMG_|PANO_|VID_'
renamed_filename = re.sub(pattern, '', filename)
if renamed_filename != filename:
    number_modified_files +=1
    os.rename(filename, renamed_filename)

Edit: Incorrect to use re.sub with os.rename

To fix this just remove the os.chdir(folder), there is no point doing this

# os.chdir(folder)
...
pattern = 'IMG_|PANO_|VID_'
renamed_filename = re.sub(pattern, '', filename)
file_path = os.path.join(folder, filename)
if renamed_filename != filename:
    number_modified_files +=1
    renamed_file_path = os.path.join(folder, renamed_filename)
    os.rename(file_path, renamed_file_path)

Regex side effect

But the regex code will work differ from your original code, as in your code, the replace end if it match in one pattern, but regex solution will try to replace all patterns in IMG_ PANO_ and VID_.

Store replace pattern in list

I suggest you use a list to store the patterns(IMG_ PANO_ and VID_)

if you wanna stop replace in the first match, use a loop to check one by one,

patterns = ["IMG_", "PANO_", "VID_"]
...
for pattern in patterns:
    if pattern in filename:
        os.rename(filename, filename.replace(pattern, ''))
        number_modified_files +=1

Or if you wanna replace all patterns, use regex

re.compile("|".join(patterns))

It is easy for only 3 patterns now, but will drive you crazy if there are 30.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ There's no point to passing the count and flags kwargs to re.sub in this case. \$\endgroup\$ – Reinderien Dec 28 '18 at 15:19
  • \$\begingroup\$ Also, don't use the re.sub form at all. Use re.compile and call sub on the compiled regex object. \$\endgroup\$ – Reinderien Dec 28 '18 at 15:20
  • \$\begingroup\$ Finally: you're applying rename and sub on the same filename, which is incorrect. sub must only be applied on the filename without path, and rename must have the path. \$\endgroup\$ – Reinderien Dec 28 '18 at 15:21
  • \$\begingroup\$ you are correct for "sub must only be applied on the filename without path", I took a mistake on it, and "There's no point to passing the count and flags kwargs to re.sub in this case. " I just showing the function, and thanks for " Use re.compile and call sub on the compiled regex object.", updating my comment \$\endgroup\$ – Aries_is_there Dec 28 '18 at 15:31
  • \$\begingroup\$ Looks OK now :) \$\endgroup\$ – Reinderien Dec 28 '18 at 22:17
-2
\$\begingroup\$

You should use a recursive function like this:

import os

def rename_files(basedir):
    for f in os.listdir(basedir):
        if os.path.isdir(f):
            rename_files(os.path.join(basedir, f))
        else:
            if "IMG_" in f:
                os.rename(f, f.replace('IMG_', ''))
            else:
                pass

rename_files('C:/Users/user/FolderParent')
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ else; pass has no effect; you can delete it and merge your else if into an elif. \$\endgroup\$ – Reinderien Dec 28 '18 at 14:34
  • \$\begingroup\$ Also, your path is wrong, because you have backslashes that will be interpreted as escapes. Use a raw string or use forward slashes. \$\endgroup\$ – Reinderien Dec 28 '18 at 14:34
  • \$\begingroup\$ Read about docs.python.org/3/library/os.html#os.scandir - it claims higher performance than listdir. \$\endgroup\$ – Reinderien Dec 28 '18 at 14:36
  • 1
    \$\begingroup\$ Finally: this solution is wholly incorrect. It doesn't have the depth check that the OP requires. \$\endgroup\$ – Reinderien Dec 28 '18 at 14:39

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.