Skip to content

Instantly share code, notes, and snippets.

@jasonbot
Last active November 8, 2025 19:20
Show Gist options
  • Select an option

  • Save jasonbot/a5e5ec420bc87ec31e9ed20ac865f981 to your computer and use it in GitHub Desktop.

Select an option

Save jasonbot/a5e5ec420bc87ec31e9ed20ac865f981 to your computer and use it in GitHub Desktop.
My work-in-progress "illegal character killer"
import collections
import collections.abc
import itertools
import os
import pathlib
import sys
REPLACE_TUPLES = (("/.", "/"),)
REPLACE_CHARS = ':*?"![]'
VERY_REPLACE = {'$': 'S'}
def walk(path: pathlib.Path) -> collections.abc.Generator[pathlib.Path]:
if not path.exists():
print(path, "went away")
return
for item in list(path.iterdir()):
if item.is_dir():
yield item
yield from walk(item)
elif item.is_file():
yield item
def need_rename(filename: pathlib.Path | str):
if not isinstance(filename, pathlib.Path):
filename = pathlib.Path(filename)
if filename.is_dir():
c = collections.Counter(f.name.lower() for f in filename.parent.iterdir())
if c[filename.name.lower()] > 1:
newfn = next(
f.name
for f in filename.parent.iterdir()
if f.name.lower() == filename.name.lower()
)
if filename.name != newfn:
return filename.parent / newfn
elif (
any(c in str(filename) for c in REPLACE_CHARS)
or any(c in str(filename) for c, _ in REPLACE_TUPLES)
or filename.name.startswith(".")
):
if filename.name.startswith("."):
filename = filename.parent / ("_" + filename.name.lstrip("."))
for c in REPLACE_CHARS:
filename = str(filename).replace(c, "_")
for c, v in VERY_REPLACE.items():
filename = str(filename).replace(c, v)
for old, new in REPLACE_TUPLES:
filename = str(filename).replace(old, new)
filename = str(filename).replace("/.", "/")
np = pathlib.Path(filename)
return np
def prune_empty_dir_up_to(base: pathlib.Path, empty_dir: pathlib.Path, *, depth=0):
if base not in empty_dir.parents:
return
if empty_dir.is_dir() and len(list(empty_dir.iterdir())) == 0:
print(f"{' ' * depth}- Unlinking {empty_dir}")
empty_dir.rmdir()
prune_empty_dir_up_to(base, empty_dir.parent, depth=depth+1)
p = pathlib.Path(".")
doit = "--do-it" in sys.argv
for file in walk(p):
if file.is_dir() and len(list(file.iterdir())) == 0:
print(file, "is an empty dir")
if doit:
prune_empty_dir_up_to(p, file)
elif rename_to := need_rename(file):
print(file, "->", rename_to)
if file.is_file():
if doit:
try:
os.makedirs(rename_to.parent)
except:
pass
os.rename(file, rename_to)
elif file.is_dir():
for item_to_move in file.iterdir():
new_path = rename_to / item_to_move.name
print(" ", item_to_move, "->", new_path)
if doit:
pp = str(new_path)
for ii in range(1, 10):
try:
os.rename(item_to_move, pp)
break
except OSError as e:
pp = f"{str(new_path)}_{ii+1}"
print(f" {e}: trying")
if doit:
os.rmdir(file)
if not doit:
print("If you like this course of events, use --do-it and rerun")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment