Github repo: https://github.com/isalevine/file-renamer
One of my hobbies is making electronic music, and thanks to resources like samplesfrommars.com, I have tons and tons of audio files to manage. One drawback of the my main drum machine is that it limits readable filenames--anything after a certain number of characters is cut off, and any name-collisions will point to the first file with that name.
A lot of my music sample library has folders full of long, repetitive filenames, like this:
I got really tired of renaming them one-by-one, so I wrote my first Python script, file-renamer, as a CLI to do mass-renaming!
file-renamer Overview
Basically, I wanted a way to change or remove specific strings from multiple filenames at once. As a CLI, file-renamer also provides a way to navigate a filesystem to modify files in different folders quickly and easily from the command line.
Setup
After cloning the repo to your computer, you can run file-renamer with $ python3 script.py
in the repo's directory.
The only configuration you will need to do is setting a default path. See script.py
line 37 (as of this writing) for the code to change:
# script.py
path = os.path.realpath('./drum-samples') # set default path here
Use
You can navigate your filesystem using the standard Unix commands pwd
, ls
, and cd
. Renaming is done inside a particular folder with the rn
or rn all
commands. To quit, enter q
or quit
.
Change Directory:
cd
List Files:
ls
Print Directory:
pwd
Rename (single file, change entire filename):
rn <current_filename.ext> <new_filename.ext>
Ex.
$ Enter command: rn Airhorn.wav AIRHOOOORN.wav
Rename All (multiple files, replace LAST string match found):
rn all <string_input> <string_output>
Ex.
$ Enter command: rn all Hihat HH
Note: Currently, rn all
will replace the LAST matching instance of <string_input>
encountered in the filename (i.e. first match encountered when reading right-to-left). See str.rpartition() in the Python docs for more information.
Exit Program:
q / quit
Python's pathlib
, os
, and os.path
modules
The functionality of this script is powered by Python's pathlib
, os
, and os.path
modules. Here's how they're used:
Navigating filesystem:
- os.path.realpath(path) - returns a string with the full filepath for a supplied file or directory
- os.path.join(path1, path2, ...) - returns a concatenated string from supplied paths (with included separators)
- Path.parent - returns a string of the path's parent directory
All of these methods return strings which can be passed to pathlib.Path(str)
to create a Path
object, which is similar to a string but has different functionality:
-
pathlib.Path(str) - creates a
Path
object out of a supplied string
In the file-renamer code, here's how they are implemented:
# script.py
import pathlib
import os
path = os.path.realpath('./drum-samples') # set default path here
current_directory = pathlib.Path(path)
...
# inside main loop, after getting user_input
if user_input[0:2] == "cd":
temp_directory = current_directory
if user_input[3:5] == "..":
new_path = current_directory.parent
current_directory = pathlib.Path(new_path)
else:
new_path = os.path.join(current_directory, user_input[3:])
current_directory = pathlib.Path(new_path)
...
if not os.path.isdir(current_directory):
print("Not a valid directory!")
current_directory = temp_directory
Here, when user_input
is cd ..
, a new_path
string is created from current_directory.parent
, and is turned back into a Path object with current_directory = pathlib.Path(new_path)
.
Otherwise, when user input is cd
followed by something else, new_path
is a string created by using os.path.join
to add the contents of user_input
onto the end of current_directory
.
Validate file and directory names:
In the snippet above, we also see that os.path.isdir()
is used to throw an error if no directory is found.
The os.path
module provides a number of useful validation methods, including two which are used in this script:
-
os.path.isdir(path) - returns
true
if the supplied path is an existing directory, orfalse
if not -
os.path.isfile(path) - returns
true
if the supplied path is an existing file, orfalse
if not
In addition to the above snippet, we see os.path.isfile()
in action here:
# script.py
# Here, array comes from using .split() on user_input
if array[1] and array[2] and os.path.isfile(array[1]):
rename_whole_filename(array[1], array[2])
else:
print("Rename aborted! (Probably because the original filename wasn't found.)")
After validating that the user_input
variable, now split into array
, contains an existing filename at array[1]
and a new filename at array[2]
, os.path.isfile(array[1])
allows us to confirm that the existing filename actually exists. Otherwise, it will return false
, which we handle with an else:
error message.
Same thing happens with os.path.isdir(current_directory)
above--it confirms that current_directory
is an existing directory.
Rename files:
Hilariously, the core functionality of this whole CLI boils down to one simple os
method:
Yep, that's it. Supply a string for src
and dst
, and os.rename()
will take care of changing the filename.
Here's how it's used in the code:
# script.py
def rename_partial_filename(filename, input, output):
src = str(filename)
head, sep, tail = src.rpartition(input)
dst = head + output + tail
os.rename(src, dst)
return dst
Here, src.rpartition(input)
allows us to search the filename-string src
from right-to-left, searching for a match for the string input
. If found, that one instance is replaced with the given string output
, and reconcatenated as the string dst
. Calling os.rename(src, dst)
will change the filename in your system.
Note: This method currently encounters errors when a file does NOT contain the given input! Instead, it will continue searching with .rpartition()
until it finds a match in its parent directories, potentially causing directory errors! You've been warned...
That's it! Now you're ready to mass-rename your files! :)
Interested in contributing?
Once again, here's the link to the GitHub repo: https://github.com/isalevine/file-renamer
Please feel free to fork/clone/hack apart as you wish! If you do, I'd love to hear how you're using the script, whether it's for music sample libraries or something else entirely.
If you'd like to tackle some specific issues with the current code, here are a few on my list:
- In
rn
andrn all
, build in handling for spaces--currently, there is no working way to remove a substring without adding any characters! - In
rn all
, build in an option to specify how many substring matches are replaced. (Originally, all matches were replaced--hence the switch to using.rpartition()
...) - Add output logs for filename changes--currently, text is output to the console, but could easily be captured in a log file.
Thanks for reading! Please feel free to share any tips or advice for working with Python's pathlib
, os
, and os.path
modules below!
Top comments (0)