I have some scripts I use a lot when renaming large numbers of files. One does simple text search and replace, the other uses Python's regular expressions. (A note on style: when writing short scripts for me, which largely fit on a screen or two, I tend to use short variable names. I have a maths background, and so 'let g be a group and let x∈g' type language is natural to me, and this extends to coding. If writing something many pages long, and possibly spread over multiple files, then as is sensible, I start using descriptive variable names.)
Examples
# replace all spaces with underscores
filesr " " "_" *
# replace all runs of weird characters with an underscore
filesrx '[^a-zA-Z0-9_\.-]+' '_' *
# replace all instances of 'turnip' or 'tomato' with vegetable
filesrx '(turnip|tomato)' 'vegetable'
# for all files with runs of 3 digits in their filename, rename to e.g. file_4562.xyz
filesrx '^.*(\d+).*' 'file_\1.xyz'
# Apply tr to filename
# change all x's to y's and all a's to b's
filetr xy ab ...
Code
These scripts use the filesr_common.py module at the bottom.
filesr
#!/usr/bin/env python3
import sys,os,re
import uuid,os.path
from filesr_common import Renamer
args = sys.argv[1:]
overwrite = False
verbose = False
if os.getenv("OVERWRITE") in ["y","Y"]:
overwrite = True
if os.getenv("VERBOSE") in ["y","Y"]:
verbose = True
forreal = True
if os.getenv("DRYRUN") is not None and os.getenv("DRYRUN") not in ["n","N"]:
forreal = False
elif os.getenv("D") is not None and os.getenv("D") not in ["n","N"]:
forreal = False
if len(args) < 3:
print("filesr searchpat replpat <files>")
if len(args) == 2:
print("(did you forget to specify files)")
sys.exit(1)
s,r = tuple(args[:2])
filenames = args[2:]
print(f"""filesr
search: {s}
replace with: {r}""")
renner = Renamer(forreal=forreal,overwrite=overwrite,verbose=verbose)
renner.sr(s,r)
renner.process(filenames)
filesrd
#!/usr/bin/env python3
import sys,os
import uuid,os.path
args = sys.argv[1:]
overwrite = False
if os.getenv("OVERWRITE") in ["y","Y"]:
overwrite = True
forreal = True
if os.getenv("DRYRUN") is not None and os.getenv("DRYRUN") not in ["n","N"]:
forreal = False
elif os.getenv("D") is not None and os.getenv("D") not in ["n","N"]:
forreal = False
if len(args) < 2:
print("filesrd chars_to_delete <files>")
sys.exit(1)
chars_to_delete = args[0]
files = args[1:]
print(f"""filesr
chars_to_delete: {chars_to_delete}""")
def rn(x,y):
if forreal:
if os.path.exists(y):
print(f"{y} exists")
if overwrite:
try:
print(f"Overwritten {y}")
os.rename(x,y)
except IsADirectoryError:
print(f"{y} is a directory")
else:
os.rename(x,y)
while True:
t = str(uuid.uuid4())
if not os.path.exists(t):
break
print("Temp name {}".format(t))
for x in files:
y = x
for c in chars_to_delete:
y = y.replace(c,"")
if x == y:
pass
elif os.path.exists(y):
print("{} already exists".format(y))
else:
xa = x.lower()
ya = y.lower()
if xa == ya:
print("Capitalisation issue")
print(f"Using temp name {t}: {x} --> {y}")
rn(x,t)
rn(t,y)
else:
print(f"Rename {x} --> {y}")
rn(x,y)
filesrx
#!/usr/bin/env python3
import sys,os,re
import uuid,os.path
from filesr_common import Renamer
args = sys.argv[1:]
overwrite = False
verbose = False
if os.getenv("OVERWRITE") in ["y","Y"]:
overwrite = True
if os.getenv("VERBOSE") in ["y","Y"]:
verbose = True
forreal = True
if os.getenv("DRYRUN") is not None and os.getenv("DRYRUN") not in ["n","N"]:
forreal = False
elif os.getenv("D") is not None and os.getenv("D") not in ["n","N"]:
forreal = False
if len(args) < 3:
print("filesrx searchpat replpat <files>")
print(" searchpad is regex")
if len(args) == 2:
print("(did you forget to specify files)")
sys.exit(1)
s,r = tuple(args[:2])
filenames = args[2:]
print(f"""filesrx
regex: {s}
replace with: {r}""")
renner = Renamer(forreal=forreal,overwrite=overwrite,verbose=verbose)
renner.srx(s,r)
renner.process(filenames)
filesrxd
#!/bin/bash
export DRYRUN=y
filesrx "$@"
filesr_common.py
#!/usr/bin/env python3
import sys,os,re
import uuid,os.path
from icecream import ic; ic.configureOutput(includeContext=True)
class Renamer:
def __init__(self,forreal = False, overwrite = False, verbose = False):
self.renner = None
self.forreal = forreal
self.verbose = verbose
self.overwrite = overwrite
def sr(self,search,replacement):
def filesr(filename):
return filename.replace(search,replacement)
self.renner = filesr
def srx(self,search,replacement):
regex = re.compile(search)
def filesr(filename):
try:
m = regex.search(filename)
if m is None and self.verbose:
print(f"File {filename} does not match {s}")
return regex.sub(replacement,filename)
except Exception as e:
ic(f"Regex {search} failed for file {filename}",e)
return None
self.renner = filesr
def tr(self,search,replacement):
trans = str.maketrans(search,replacement)
def filesr(filename):
return filename.translate(trans)
self.renner = filesr
def process(self,filenames):
while True:
t = str(uuid.uuid4())
if not os.path.exists(t):
break
print("Temp name {}".format(t))
temp_name = t
for x in filenames:
y = self.renner(x)
if y is None:
print(f"replacment func failed for {x}")
continue
if x == y:
pass
elif os.path.exists(y):
sx = os.stat(x)
sy = os.stat(y)
if not sx.st_ino == sy.st_ino:
print("{} already exists".format(y))
else: # x and y are the same file
print("Capitalisation issue")
print(f"Using temp name {t}: {x} --> {y}")
self.rn(x,t)
self.rn(t,y)
else:
print(f"Rename {x} --> {y}")
self.rn(x,y)
def rn(self,x,y):
if x == y:
print(f"No change in {x}")
return
if self.forreal:
if os.path.exists(y):
print(f"{y} exists")
if self.overwrite:
try:
print(f"Overwritten {y}")
os.rename(x,y)
except IsADirectoryError:
print(f"{y} is a directory")
else:
os.rename(x,y)