In any discussion on bulk-renaming, I’d be remiss if I didn’t bring up the very excellent mmv
family of tools. — “mmv” for “mass move” or “multiple move”, there are also obvious and analogous mcp
and mln
forms.
For most types of bulk “name-mangling”, mmv
is my go-to tool. Though, in truth, it’s actually 100% useless for solving this particular problem. (mmv
uses wildcard-style patterns, which means the one thing it can’t handle is regexp-style filtering of arbitrary numbers of a specific character (or characters) found in arbitrary locations throughout the source string.)
But for anything that involves pattern-based matching, deconstruction, and reassembly, mmv
is the bomb.
Basically, it extends the shell’s familiar wildcard matching: ?
(match-1), *
(match 0-∞), [...]
(match any of a set of characters) by adding a reference syntax that lets you build output templates based on your input patterns. (It’s a simple 1-based numeric index, every #n in the output pattern refers to whatever was matched by the nth wildcard in the source pattern.)
It can also operate recursively, and adds an additional wildcard ;
that can only be either the very first pattern character, or immediately follow a slash. A ;
will expand to a directory path of any arbitrary length — basically, mmv ';*.c'
would match the same as zsh’s **/*.c
, but also (like the other wildcards) stores what it matches for later reference.
The syntax opens up all sort of neat tricks. (With patterns that are always single-quoted, for the exact reasons discussed above. You don’t ever want the shell touching these.) You can do things like:
# Rename all *.mp4 to *.m4a, in the same directory
mmv '*.mp4' '#1.m4a'
# do it recursively...
mmv ';*.mp4' '#1#2.m4a' # #1 is now what ; matched, incl. /
# do it recursively across the *entire* filesystem
mmv '/;*.mp4' '/#1#2.m4a'
# Discover music files in a hierarchy where tracks are
# organized into directories by Artist:
# Pharrell/Probably Something Autotuned.mp4
# but some of those contain album subdirectories:
# Adele/Eleventy/Probably Something Weepy.mp3
# ...and collect it all into the current directory as:
# Artist - Title.xtn
mcp '*/;*.m??' './#1 - #3.m#4#5'
# (where...)
# #1 - is the * that matched the Artist directory name
# #2 - is the ; match, either nothing or the Album/ subdir,
# so we discard it either way
# #3 - is the * after the ;, which matches from the start
# of the filename up to the ".m" of the extension
# #4#5 - are ??, the other two characters of the extension
Finally…
# Rename all TV series episode files that use the format:
# Series.sNNeMM.Episode.Title.Format.Scene.Noise.mkv
# into the alternative format:
# series.NxMM.episode.title.blah.blah.blah.mkv
# and lowercase any capitalized names along the way
mmv '*[Ss]0[0-9][eE][0-9][0-9]*.mkv' '#l1#3x#5#6#l7.mkv'
mmv '*[sS][1-9][0-9][eE][0-9][0-9]*.mkv' '#l1#3#4x#6#7#l8.mkv'
Let’s try that last pair:
$ ls *.mkv
some.sequel.s01e01.itll.never.last.1080p.HiMOM.mkv
Some.Show.S01E22.The.Last.Episode.720p.J011Y.R0GR.mkv
the.after.show.s14e01.all.new.cast.576i.DVDrip.mkv
The.After.Show.S14E02.Part.Deux.4K.240Hz.Humblebrag.mkv
$ mmv -v '*[Ss]0[0-9][eE][0-9][0-9]*.mkv' '#l1#3x#5#6#l7.mkv'
Some.Show.S01E22.The.Last.Episode.720p.J011Y.R0GR.mkv -> some.show.1x22.the.last.episode.720p.j011y.r0gr.mkv : done
some.sequel.s01e01.itll.never.last.1080p.HiMOM.mkv -> some.sequel.1x01.itll.never.last.1080p.himom.mkv : done
$ mmv -v '*[sS][1-9][0-9][eE][0-9][0-9]*.mkv' '#l1#3#4x#6#7#l8.mkv'
The.After.Show.S14E02.Part.Deux.4K.240Hz.Humblebrag.mkv -> the.after.show.14x02.part.deux.4k.240hz.humblebrag.mkv : done
the.after.show.s14e01.all.new.cast.576i.DVDrip.mkv -> the.after.show.14x01.all.new.cast.576i.dvdrip.mkv : done
$ ls *.mkv
some.sequel.1x01.itll.never.last.1080p.himom.mkv
some.show.1x22.the.last.episode.720p.j011y.r0gr.mkv
the.after.show.14x01.all.new.cast.576i.dvdrip.mkv
the.after.show.14x02.part.deux.4k.240hz.humblebrag.mkv