4

How can we replace the whitespaces in the names of folders, subfolders and files in a given parent folder?

My initial attempt to replace up to level 8 is given below. I am sure there are better ways. My code looks ugly. Better solutions are more than welcome.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#


def replace_space_by_underscore(path):
    """Replace whitespace in filenames by underscore."""
    import glob
    import os
    for infile in glob.glob(path):
        new = infile.replace(" ", "_")
        try:
            new = new.replace(",", "_")
        except:
            pass
        try:
            new = new.replace("&", "_and_")
        except:
            pass
        try:
            new = new.replace("-", "_")
        except:
            pass
        if infile != new:
            print(infile, "==> ", new)
        os.rename(infile, new)

if __name__ == "__main__":
    try:
        replace_space_by_underscore('*/*/*/*/*/*/*/*')
    except:
        pass
    try:
        replace_space_by_underscore('*/*/*/*/*/*/*')
    except:
        pass
    try:
        replace_space_by_underscore('*/*/*/*/*/*')
    except:
        pass
    try:
        replace_space_by_underscore('*/*/*/*/*')
    except:
        pass
    try:
        replace_space_by_underscore('*/*/*/*')
    except:
        pass
    try:
        replace_space_by_underscore('*/*/*')
    except:
        pass
    try:
        replace_space_by_underscore('*/*')
    except:
        replace_space_by_underscore('*')
BhishanPoudel
  • 15,974
  • 21
  • 108
  • 169

3 Answers3

10

You could use os.walk that allows you to change the names of the iterated folders on the fly:

import os

def replace(parent):
    for path, folders, files in os.walk(parent):
        for f in files:
            os.rename(os.path.join(path, f), os.path.join(path, f.replace(' ', '_')))
        for i in range(len(folders)):
            new_name = folders[i].replace(' ', '_')
            os.rename(os.path.join(path, folders[i]), os.path.join(path, new_name))
            folders[i] = new_name

os.walk iterates directory tree starting from parent in top-down order. For every folder it returns tuple (current path, list of files, list of folders). Given folder list can be mutated and os.walk will use the mutated contents in the following steps of the iteration.

Folder before run:

.
├── new doc
└── sub folder
    ├── another folder
    ├── norename
    └── space here

After:

.
├── new_doc
└── sub_folder
    ├── another_folder
    ├── norename
    └── space_here
niemmi
  • 17,113
  • 7
  • 35
  • 42
1

You need a recursive solution. Rename all files in the current directory; then for each subdirectory (if any), descend into that subdirectory X (with os.chdir(X)), call the same function again, and ascend back to the parent directory (with os.chdir("..")).

DYZ
  • 55,249
  • 10
  • 64
  • 93
1

Following the exact idea of @niemmi I ended up with this:

Warning: Never run this script from HOME directory or from some important directory, it will RENAME all files including HIDDEN files.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Date: Dec 15, 2016


def replace_space_by_underscore(parent):
    """Replace whitespace by underscore in all files and folders.

    replaces    , - [ ] () __   ==>  underscore

    """
    import os
    for path, folders, files in os.walk(parent):
        # rename the files
        for f in files:
            old = os.path.join(path, f)
            bad_chars = [r' ', r',', r'-', r'&', r'[', r']', r'(', r')', r'__']
            for bad_char in bad_chars:
                if bad_char in f:
                    new = old.replace(bad_char, '_')
                    print(old, "==>", new)
                    os.rename(old, new)

        # rename the folders
        for i in range(len(folders)):
            new_name = folders[i].replace(' ', '_')
            bad_chars = [r' ', r',', r'-', r'&',
                         r'[', r']', r'(', r')', r'__']
            for bad_char in bad_chars:
                if bad_char in new_name:
                    new_name = new_name.replace(bad_char, '_')
                    print(folders[i], "==> ", new_name)
            old = os.path.join(path, folders[i])
            new = os.path.join(path, new_name)
            os.rename(old, new)
            folders[i] = new_name


if __name__ == "__main__":
    replace_space_by_underscore('.')
BhishanPoudel
  • 15,974
  • 21
  • 108
  • 169
  • Good solution - just needs `old = new` under `os.rename(old, new)` in the files for loop (ie: files with multiple `bad_chars`) – lys Dec 15 '21 at 23:40