{"id":19,"date":"2014-01-25T09:28:00","date_gmt":"2014-01-24T23:28:00","guid":{"rendered":"https:\/\/www.serenux.com\/?p=19"},"modified":"2021-01-11T09:33:51","modified_gmt":"2021-01-10T23:33:51","slug":"howto-rename-multiple-tv-episode-files-with-one-command","status":"publish","type":"post","link":"https:\/\/www.serenux.com\/index.php\/2014\/01\/25\/howto-rename-multiple-tv-episode-files-with-one-command\/","title":{"rendered":"HowTo: Rename multiple TV episode files with one command"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">So, you\u2019ve procured a number of files that represent a pre-recorded television series and they are typically named something like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">MyTVShow.Original.S01E01-720p.HDTV.x264-ImAwesome.mkv\nMyTVShow.Original.S01E02-720p.HDTV.x264-ImAwesome.mkv\nMyTVShow.Original.S01E03-720p.HDTV.x264-ImAwesome.mkv\nMyTVShow.Original.S01E04-720p.HDTV.x264-ImAwesome.mkv<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You\u2019d like to rename them to look something like this instead:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">MyTVShow&nbsp;S01E01.mkv\nMyTVShow&nbsp;S01E02.mkv\nMyTVShow&nbsp;S01E03.mkv\nMyTVShow&nbsp;S01E04.mkv<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Normally you\u2019d tediously edit each filename and rename them manually, but to do so individually across a large folder will take a long time. Surely there\u2019s a way to rename them all in a more convenient manner?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There certainly is! And with just one command too\u2026<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One command? How can you possibly do that, you ask? Well, Linux commands generally have the ability to accept a regular or Perl expression to allow for complex matching tasks, simplifying your work, and we will utilise that here with the rename command\u2019s ability to parse Perl expressions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Note: As I understand it, only Debian-based distros such as Ubuntu carry the \u201crename\u201d command as part of the default command set. Other distros may require you to add the command binary to your system.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The task in this case is to take a string to match on, in this case \u201cMyTVShow.Original.\u201d, take a wildcard match of the \u201cSxxEyy\u201d part (where \u201cxx\u201d and \u201cyy\u201d are any numeric characters), ignore everything after that and then create a new string for the filename that is formatted as \u201cMyTVShow SxxEyy.mkv\u201d and replace \u201cxx\u201d and \u201cyy\u201d with the Season and Episode number picked up in the wildcard match.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And even better than that, we will write this in such a way that we can support any file extension so it doesn\u2019t matter if the folder contains \u201c.mkv\u201d or \u201c.avi\u201d or \u201c.mp4\u2033 files, they will all be preserved in the rename.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the command you need:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">rename -n 's\/MyTVShow\\.Original\\.[Ss](\\d{2})[Ee](\\d{2}).+(\\.\\D{3})\/MyTVShow\\ S$1E$2$3\/' *<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When you run it, you will see output as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$&nbsp;rename -n 's\/MyTVShow\\.Original\\.[Ss](\\d{2})[Ee](\\d{2}).+(\\.\\D{3})\/MyTVShow\\ S$1E$2$3\/' *\nMyTVShow.Original.S01E01-720p.HDTV.x264-ImAwesome.mkv renamed as&nbsp;MyTVShow&nbsp;S01E01.mkv\nMyTVShow.Original.S01E02-720p.HDTV.x264-ImAwesome.mkv renamed as&nbsp;MyTVShow&nbsp;S01E02.mkv\nMyTVShow.Original.S01E03-720p.HDTV.x264-ImAwesome.mkv renamed as&nbsp;MyTVShow&nbsp;S01E03.mkv\nMyTVShow.Original.S01E04-720p.HDTV.x264-ImAwesome.mkv renamed as&nbsp;MyTVShow&nbsp;S01E04.mkv<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But hang on, when I list my files, they\u2019ve still got the old names! What gives?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The \u201c-n\u201d parameter tells the rename command not to actually action any rename, but show you what it would have acted on \u2013 good for testing just in case you made a mistake in your match criteria. Once you are ready to commit the rename, either remove the \u201c-n\u201d parameter or if you\u2019d like to see what the rename command is doing, replace it with \u201c-v\u201d for verbose output instead (it\u2019s the same as \u201c-n\u201d\u2018s output).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So exactly how does this command work? Let\u2019s break it down a bit:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span class=\"has-inline-color has-vivid-red-color\">rename -n \u2018s\/MyTVShow\\.Original\\.<\/span><\/strong>[Ss](\\d{2})[Ee](\\d{2}).+(\\.\\D{3})\/MyTVShow\\ S$1E$2$3\/\u2019 *<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The first part specifies the rename command, the parameters to use and specifies the starting string we need to match on. You will notice that there are backslashes before the full stops. This is needed because the full stop character has other functions in an expression and so to specify it as a literal character, we need to \u201cescape\u201d out of the expression mode into \u201cliteral\u201d mode, and that is facilitated by the blackslash. The character that follows the backslash is to be taken literally as part of the string we are constructing. If you wanted to literally specify a backslash character, for example, you\u2019d have to specify a double backslash, ie: \u201c\\\\\u201d.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">rename -n \u2018s\/MyTVShow\\.Original\\.<strong><span class=\"has-inline-color has-vivid-red-color\">[Ss](\\d{2})[Ee](\\d{2}).+(\\.\\D{3})<\/span><\/strong>\/MyTVShow\\ S$1E$2$3\/\u2019 *<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>This part matches the \u201cSxxEyy\u201d part of the filename. This is not a literal string in this case because we need to dynamically cater for the different Season and Episode numbers in each filename. In addition to that, Linux filenames are case-sensitive, so we also need to consider that the \u201cS\u201d and \u201cE\u201d parts could be upper or lowercase, so we start with \u201c[Ss]\u201d which means \u201cmatch either a capital \u2018S\u2019 or a lowercase \u2018s\u2019 please\u201d, then we specify that we wish to wildcard the next two digits by specifying \u201c(\\d{2})\u201d. The \u201c\\d\u201d means to match any numerical digit and is essentially a shorthand version of \u201c[0-9]\u201d (it also matches other language numerical digits beyond the English 0-9, but I digress). The \u201c{2}\u201d part means apply that digit match to the next two characters, so we match on \u201c01\u201d or \u201c02\u201d or \u201c03\u201d, etc.We apply the same process to the \u201cEyy\u201d part of the string, again matching either a capital \u201cE\u201d or a lowercase \u201ce\u201d followed by any two numerical digits.Finally, we accept the remainder of the filename up to the filename extension with the \u201c.\u201d part (because we don\u2019t care what\u2019s here) and explicitly wildcard match the extension with the \u201c+(\\.\\D{3})\u201d part, which allows us to match any three character file extension (not just numbers), for example \u201c.mkv\u201d, \u201c.MKV\u201d, \u201c.avi\u201d, \u201c.mpg\u201d, etc, however it will NOT match \u201c.mpeg\u201d or \u201c.jpeg\u201d or \u201c.myfilextension\u201d because those are larger than three character extensions that while they form a part of perfectly legal filenames, they are also very uncommon. In this case we will ignore them and assume that we\u2019ll only ever deal with three character file extensions.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">rename -n \u2018s\/MyTVShow\\.Original\\.[Ss](\\d{2})[Ee](\\d{2}).+(\\.\\D{3})<strong><span class=\"has-inline-color has-vivid-red-color\">\/MyTVShow\\ S$1E$2$3\/\u2019\u00a0<\/span><\/strong>*<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Now that we\u2019ve specified our matching criteria, what do we do when we\u2019ve found that match? We need to tell the rename command what we\u2019re going to be renaming it to. The first forward slash means we\u2019ve finished specifying our match criteria and are now specifying our rename string and it starts off with \u201cMyTVShow\u201d followed by an escape to be able to specify a space before the \u201cSxxEyy\u201d bit. We then specify the \u201cSxxEyy\u201d bit as \u201cS$1E$2\u2033.Now you didn\u2019t know this, but in the previous matching criteria, the digit wildcard matches were automatically stored as variables because they were surrounded by parenthesis \u201c(\u201d and \u201c<img decoding=\"async\" src=\"https:\/\/web.archive.org\/web\/20150206044333im_\/http:\/\/www.serenux.com\/wp-content\/themes\/grey-opaque\/images\/smilies\/icon_wink.gif\" alt=\"Smilie: ;)\">\u201d. Variables generated start at $1 and continue up as $2, $3, etc. You can now specify those variables as part of your destination filename and in this case, the Season number that was wildcard matched from the original filename is now specified as the first variable $1 in your destination filename, thus if \u201c03\u201d was in the original filename as the Season number, you can now write \u201c03\u201d as part of your destination filename. Ditto the Episode number as variable $2.Finally, we end the destination string with a straight up \u201c$3\u2033 that will specify the third variable we created which is the filename extension picked up in the wildcard match, which in our tutorial example will be \u201c.mkv\u201d. The beauty of this is that if you had multiple files with different file extensions, those extensions will be completely preserved.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">rename -n \u2018s\/MyTVShow\\.Original\\.[Ss](\\d{2})[Ee](\\d{2}).+(\\.\\D{3})\/MyTVShow\\ S$1E$2$3\/\u2019\u00a0<strong><span class=\"has-inline-color has-vivid-red-color\">*<\/span><\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>And finally, we specify which files the rename command will be applied to as a whole. In this case because we have an expression to do the matching, we don\u2019t need to use traditional filename matching, so we just specify an asterisk to apply to \u201call files\u201d because any file that does not match the search expression will be ignored.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Optional: Getting&nbsp;<em>slightly<\/em>&nbsp;trickier, but for good reason\u2026<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s say you deal with a&nbsp;<em>lot<\/em>&nbsp;of these files that you have to rename with different names on a regular basis. While the above tutorial has saved you a fair bit of time, at the moment you\u2019re always having to copy and paste the command down and then modifying it to suit the filename strings you are changing from and to, remembering to escape certain characters so that they are processed properly like the full stops. We can make this process even more convenient by using a Bash script to allow us to simply specify a command followed by old and new strings only and not have to worry about escape characters.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Create a new file using your text editor (you can name it anything you like and it does not have to have \u201c.sh\u201d on the end):<br><br><code>$ sudo nano \/usr\/bin\/fixfilenames.sh<\/code><br><\/li><li>Type or copy and paste in the following:<br><br><code>#!\/bin\/bash<br># =============================================================<br># Bulk rename TV series files. Written by HyRax, January 2014<\/code><br><code># ============================================================== <br>if [ $# == 0 ]; then<br>    echo \"\"<br>    echo \"Usage: $0 OLDNAME NEWNAME\"<br>    echo \"\"<br>    echo \" Eg: If the original filename is 'MyTVShow.Original.S01E01-720p.HDTV.ImAwesome.mkv' then\"<br>    echo \" specify the command as '$0 MyTVShow.Original. MyTVShow' which will rename\"<br>    echo \" all the files into 'MyTVShow SxxEyy.mkv' format. Note that a space is assumed as wanted between\"<br>    echo \" the new name and the SxxEyy part. The Season and Episode numbers along with the file extensions\"<br>    echo \" will be preserved in each rename.\"<br>    echo \"\"<br>    exit<br>fi<br><br>cmd=\"rename -v 's\/$1[Ss](\\d{2})[Ee](\\d{2}).+(\\.\\D{3})\/$2 S\\$1E\\$2\\$3\/' *\"<br>echo \"\"<br>echo \"Renaming files... (Renamed files will be listed below. Nothing listed means nothing actioned!)\"<br>eval $cmd<br>echo \"Done.\"<\/code><br><\/li><li>Save your changes by pressing CTRL+X, then Y and then Enter.<br><\/li><li>Now let\u2019s make the new script executable:<br><br><code>$ sudo chmod +x \/usr\/bin\/fixfilenames.sh<\/code><br><\/li><li>Now you can rename your files as follows (using the tutorial\u2019s filename example):<br><br><code>$ fixfilenames.sh MyTVShow.Original. MyTVShow<\/code><br><\/li><li>List your directory and you will see all your files renamed successfully!<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Pat yourself on the back. You\u2019re done. If you\u2019d like to explore more about expressions and what they can do, there are dozens of general tutorials around on the web which will easily teach you how to modify your expression to cover even more complex matches should you have that need.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So, you\u2019ve procured a number of files that represent a pre-recorded television series and they are typically named something like this: MyTVShow.Original.S01E01-720p.HDTV.x264-ImAwesome.mkv MyTVShow.Original.S01E02-720p.HDTV.x264-ImAwesome.mkv MyTVShow.Original.S01E03-720p.HDTV.x264-ImAwesome.mkv MyTVShow.Original.S01E04-720p.HDTV.x264-ImAwesome.mkv You\u2019d like to rename them to look something like this instead: MyTVShow&nbsp;S01E01.mkv MyTVShow&nbsp;S01E02.mkv MyTVShow&nbsp;S01E03.mkv MyTVShow&nbsp;S01E04.mkv Normally you\u2019d tediously edit each filename and rename them manually, but to do so individually [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-19","post","type-post","status-publish","format-standard","hentry","category-tutorials"],"_links":{"self":[{"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/posts\/19","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/comments?post=19"}],"version-history":[{"count":1,"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/posts\/19\/revisions"}],"predecessor-version":[{"id":20,"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/posts\/19\/revisions\/20"}],"wp:attachment":[{"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/media?parent=19"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/categories?post=19"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.serenux.com\/index.php\/wp-json\/wp\/v2\/tags?post=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}