Have you ever had a huge complicated folder tree with thousands of files buried layers deep in folders-within-folders?
And wanted to flatten them out so all the files are in a single folder, but without filename collisions (that is, lots of files with the same name)?
I did. The closest thing I could find off-the-shelf was this, so I wrote the Python script below.
I hope it’s helpful to someone.
To run it (assuming you have Python installed – you’ll need Python 3 in case some of your pathnames have Unicode characters), do this:
python flatten.py [root folder to be flattened] DOIT
If you leave off “DOIT” (all caps), then it will simulate what it’s going to do, without actually doing anything.
The way it works is by copying all the files to the root folder, renaming them with the original path to the file, but substituting “¦” for the “/” or “\” (Windows, Unix respectively).
So if you have (using the example from the link above) this inside “Folder0”:
Folder0 Folder1 File1.1.txt File1.2.txt FolderA FileA.txt FolderB FileB.1.txt FileB.2.txt Folder2 FolderC FileC.txt
python flatten.py c:\path\to\Folder0 DOIT
Gets you these six files inside Folder0:
Folder1¦File1.1.txt Folder1¦File1.2.txt Folder1¦FolderA¦FileA.txt Folder1¦FolderB¦FileB.1.txt Folder1¦FolderB¦FileB.2.txt Folder2¦FolderC¦FileC.txt
Enjoy, and if you make improvements, please post a comment here.
# -*- coding: utf-8 -*- # for Python3 (needs Unicode) import os, shutil, sys def splitPath(p): """Returns a list of folder names in path to p From user1556435 at http://stackoverflow.com/questions/3167154/how-to-split-a-dos-path-into-its-components-in-python""" a,b = os.path.split(p) return (splitPath(a) if len(a) and len(b) else ) + [b] def safeprint(s): """This is needed to prevent the Windows console (command line) from choking on Unicode characters in paths. From Giampaolo Rodolà at http://stackoverflow.com/questions/5419/python-unicode-and-the-windows-console""" try: print(s) except UnicodeEncodeError: if sys.version_info >= (3,): print(s.encode('utf8').decode(sys.stdout.encoding)) else: print(s.encode('utf8')) def flatten(root, doit): """Flattens a directory by moving all nested files up to the root and renaming uniquely based on the original path. Converts all occurances of "SEP" to "REPL" in names (this allows un-flatting later, but at the cost of the replacement). If doit is True, does action; otherwise just simulates. """ SEP = "¦" REPL = "?" folderCount = 0 fileCount = 0 if not doit: print("Simulating:") for path, dirs, files in os.walk(root, topdown=False): if path != root: for f in files: sp = splitPath(path) np = "" for element in sp[1:]: e2 = element.replace(SEP, REPL) np += e2 + SEP f2 = f.replace(SEP, REPL) newName = np + f2 safeprint("Moved: "+ newName ) if doit: shutil.move(os.path.join(path, f), os.path.join(root, newName)) fileCount += 1 safeprint("Removed: "+ path) if doit: os.rmdir(path) folderCount += 1 if doit: print("Done.") else: print("Simulation complete.") print("Moved files:", fileCount) print("Removed folders:", folderCount) """ if __name__ == "__main__": print("") print("Flatten v1.00 (c) 2014 Nerdfever.com") print("Use and modification permitted without limit; credit to NerdFever.com requested.") if len(sys.argv) < 2: print("Flattens all files in a path recursively, moving them all to the") print("root folder, renaming based on the path to the original folder.") print("Removes all now-empty subfolders of the given path.") print("") print("Syntax: flatten [path] [DOIT]") print("") print("The DOIT parameter makes flatten do the action; without it the action is only simualted.") print("Examples:") print(" flatten //machine/path/foo Simulates flattening all contents of the given path") print(" flatten //machine/path/bar DOIT Actually flattens given path") else: if len(sys.argv) == 3 and sys.argv == "DOIT": flatten(sys.argv, True) else: flatten(sys.argv, False)