Skip to content

Instantly share code, notes, and snippets.

@yanli0303
Created August 17, 2025 01:42
Show Gist options
  • Select an option

  • Save yanli0303/341a79363d593592ab689a50a2897dec to your computer and use it in GitHub Desktop.

Select an option

Save yanli0303/341a79363d593592ab689a50a2897dec to your computer and use it in GitHub Desktop.
Directory Flattening Script, it flattens a directory by moving all files from subdirectories to the root directory
#!/usr/bin/env python3
"""
Directory Flattening Script
This script accepts a path to a directory and flattens it by moving all files
from subdirectories to the root directory. When file name conflicts occur,
it keeps all files by adding numbered suffixes like (1), (2), etc.
Usage:
python flat_dir.py <directory_path>
"""
import os
import shutil
import sys
from pathlib import Path
def get_unique_filename(target_dir: Path, filename: str, used_names: set[str]) -> str:
"""
Generate a unique filename by adding numbered suffixes if conflicts exist.
Args:
target_dir: The target directory path
filename: The original filename
used_names: set of already used filenames to avoid conflicts
Returns:
A unique filename
"""
if filename not in used_names and not (target_dir / filename).exists():
used_names.add(filename)
return filename
# Split filename into name and extension
name_part, ext_part = os.path.splitext(filename)
counter = 1
while True:
new_filename = f"{name_part}({counter}){ext_part}"
if new_filename not in used_names and not (target_dir / new_filename).exists():
used_names.add(new_filename)
return new_filename
counter += 1
def flat_dir(directory_path: str) -> None:
"""
Flatten a directory by moving all files from subdirectories to the root.
Args:
directory_path: Path to the directory to flatten
"""
root_path = Path(directory_path).resolve()
if not root_path.exists():
print(f"Error: Directory '{directory_path}' does not exist.")
return
if not root_path.is_dir():
print(f"Error: '{directory_path}' is not a directory.")
return
print(f"Flattening directory: {root_path}")
# Keep track of used filenames to avoid conflicts
used_names: set[str] = set()
# First, collect all existing files in the root directory
for item in root_path.iterdir():
if item.is_file():
used_names.add(item.name)
# Collect all files from subdirectories
files_to_move = []
for root, dirs, files in os.walk(root_path):
current_path = Path(root)
# Skip the root directory itself
if current_path == root_path:
continue
for file in files:
file_path = current_path / file
files_to_move.append(file_path)
if not files_to_move:
print("No files found in subdirectories to move.")
return
print(f"Found {len(files_to_move)} files to move from subdirectories.")
# Move files to root directory
moved_count = 0
error_count = 0
for file_path in files_to_move:
try:
# Generate unique filename
unique_filename = get_unique_filename(root_path, file_path.name, used_names)
target_path = root_path / unique_filename
# Move the file
shutil.move(str(file_path), str(target_path))
if unique_filename != file_path.name:
print(f"Moved: {file_path.relative_to(root_path)} -> {unique_filename}")
else:
print(f"Moved: {file_path.relative_to(root_path)}")
moved_count += 1
except Exception as e:
print(f"Error moving {file_path.relative_to(root_path)}: {e}")
error_count += 1
# Remove empty directories
empty_dirs_removed = 0
for root, dirs, files in os.walk(root_path, topdown=False):
current_path = Path(root)
# Skip the root directory itself
if current_path == root_path:
continue
try:
if not any(current_path.iterdir()): # Directory is empty
current_path.rmdir()
print(f"Removed empty directory: {current_path.relative_to(root_path)}")
empty_dirs_removed += 1
except OSError as e:
# Directory might not be empty or other permission issues
print(f"Could not remove directory {current_path.relative_to(root_path)}: {e}")
print("\n" + "=" * 50)
print("Flattening complete!")
print(f"Files moved: {moved_count}")
print(f"Errors: {error_count}")
print(f"Empty directories removed: {empty_dirs_removed}")
def main():
"""Main function to handle command line arguments and run the flattening process."""
if len(sys.argv) != 2:
print("Usage: python flat_dir.py <directory_path>")
print("\nExample:")
print(" python flat_dir.py /path/to/directory")
return
directory_path = sys.argv[1]
# Confirm with user before proceeding
print(f"This will flatten the directory: {directory_path}")
print("All files from subdirectories will be moved to the root directory.")
print("Empty subdirectories will be removed.")
print("This operation cannot be easily undone.")
response = input("\nDo you want to continue? (y/N): ").strip().lower()
if response not in ["y", "yes"]:
print("Operation cancelled.")
return
flat_dir(directory_path)
if __name__ == "__main__":
main()
@yanli0303
Copy link
Copy Markdown
Author

yanli0303 commented Aug 17, 2025

uv run https://gist.github.com/yanli0303/341a79363d593592ab689a50a2897dec/raw/
Usage: python flat_dir.py <directory_path>

Example:
  python flat_dir.py /path/to/directory

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment