I have a lot of files I want to rename and it would take me a long time to do them manually. They are video files and are usually in this format - \"NAME OF SHOW - EPISODE N
The following solution:
107
for season 1, episode 7, or 1002
for season 10, episode 2)find
and bash
techniques, such as:
-regex
primary to match filenames by regular expression (rather than wildcard pattern, as with -name
)execdir
to execute a command in the same directory as each matching file (where {}
contains the matching file name only)bash
script that demonstrates regular-expression matching with =~
and capture groups reported via the built-in ${BASH_REMATCH[@]}
variable; command substitution ($(...)
) to left-pad a value with zeros; variable expansion to extract substrings (${var:n[:m]}
).# The regular expression for matching filenames (without paths) of interest:
# Note that the regex is partitioned into 3 capture groups
# (parenthesized subexpressions) that span the entire filename:
# - everything BEFORE the season+episode specifier
# - the season+episode specifier,
# - everything AFTER.
# The ^ and $ anchors are NOT included, because they're supplied below.
fnameRegex='(.+ - )([0-9]{3,4})( - .+)'
# Find all files of interest in the current directory's subtree (`.`)
# and rename them. Replace `.` with the directory of interest.
# As is, the command will simply ECHO the `mv` (rename) commands.
# To perform the actual renaming, remove the `echo`.
find -E . \
-type f -regex ".+/${fnameRegex}\$" \
-execdir bash -c \
'[[ "{}" =~ ^'"$fnameRegex"'$ ]]; se=$(printf "%04s" "${BASH_REMATCH[2]}");
echo mv -v "{}" "${BASH_REMATCH[1]}S${se:0:2}E${se:2}${BASH_REMATCH[3]}"' \;
Using the perl
rename
implementation which easily can take care of proper padding and works for any number of seaons and episodes (<100, but can easily be adapted to your current format):
$ ls -1 *.avi
My Show - 0301 - Qux.avi
My Show - 101 - Foo.avi
My Show - 102 - Bar.avi
My Show - 1102 - Blah.avi
My Show - 201 - Quux.avi
$ rename -n 's/- (\d+)(\d{2,}) -/sprintf("- S%02dE%02d -", $1, $2)/e' *.avi
My Show - 0301 - Qux.avi renamed as My Show - S03E01 - Qux.avi
My Show - 101 - Foo.avi renamed as My Show - S01E01 - Foo.avi
My Show - 102 - Bar.avi renamed as My Show - S01E02 - Bar.avi
My Show - 1102 - Blah.avi renamed as My Show - S11E02 - Blah.avi
My Show - 201 - Quux.avi renamed as My Show - S02E01 - Quux.avi
I think homebrew ships with the correct version, so it's just a matter of installing via
$ brew install rename
for FOO in *; do mv "$FOO" "`echo $FOO | sed 's/\([^-]*\) - \([0-9]\)\([0-9][0-9]\)\(.*\)/\1 - S0\2E\3\4/g'`" ; done
This works if there are less than 10 seasons.
The following script will find all .mp4 with one string of 3 consecutive numbers. Example something.111.mp4 . It will convert it to something.S01E11.mp4 . It will also exclude any sample files.
find . ! -name "*sample*" -name '*.[0-9][0-9][0-9].*.mp4' -type f | while read filename; do mv -v "${filename}" "`echo $filename | sed -e 's/[0-9]/S0&E/;s/SS00E/S0/g'`";done;
Like the previous script it will only work if less than 10 seasons.
For those that are trying to personalize for their current directory tree, I would recommend learning the sed and find command. They are pretty powerful, are simple, and will allow you to substitute any string within a file name.