How replace all dots AFTER other chars got captured

I’m trying to rename some shows with the perl-rename command.
The format currently looks something like this:

series.S01E**.title.of.the.episode.mkv

The asterisks are place holders for the episode number.

I want to rename it like so:

[Series] - S01E** - title of the episode.mkv

I’ve managed to capture the name of the series as well as the episode, but i don’t know how to replace the dots after i already matched something.

perl-rename 's/(w+).(Sd{2}Ed{2})./[u$1] - $2 - /' -n *.mkv
                                     ^ No idea what to insert here

One call of rename is my goal.
The title may be any variable number of words long, so i cannot do something like (w+).(w+).../$1 $2... # match a word and a dot for a fixed amount of times.

What I’m looking for is basically a loop to match a pattern of word..

Asked By: Zacki

||

Try:

perl-rename -n 's{(w+).(Sd{2}Ed{2}).(.*)(.mkv)Z}{
  "[u$1] - $2 - " . ($3 =~ y/./ /r) . $4}es' ./*.mkv
  • perl-based rename implementations (all derived from the rename example script that had been shipped with perl from as far back as perl 3 in 1989), rename the files given as arguments to the value of $_ after it has been modified by the perl code given in the first argument.
  • s{regex}{replacement}flags, same as s/regex/replacement/flags is short for $_ =~ s{regex}{replacement}flag. That is, the substitution operator is applied to the $_ variable which contains the name the file is to be renamed to.
  • Here flags contains e which means the replacement is interpreted as perl code which is evaluated to generate the replacement.
  • We also use the s flag which causes . to also match the newline character which you generally want to use with rename as newline is as valid a character as any in a file name. It’s also why we use Z (matches at the end of the subject) instead of $ which in perl matches at the end of the subject but also possibly before a newline that is at the end of the subject. In this case though, it won’t make a difference as all the file names end in v, not newline.
  • ($3 =~ y/./ /r) applies the y (aka tr) operator to the third capture group. With the r flag, the substitution is not applied in place ($3 is read-only anyway), but the result of the substitution is returned instead.

Personally, I’d use zsh‘s zmv instead:

autoload -Uz zmv
zmv -n '(*).(S<1-99>E<1-99>).(*)(.mkv)' '[${(C)1}] - $2 - ${3//./ }$4'

Where ${(C)1} capitalises every word (sequences of alnums) in the part before ./SXXEYY (changes foo.Bar-BAZ to Foo.Bar-Baz for instance).

Advantages over rename:

  • there’s only one implementation (whilst there are dozens of variants of rename).
  • it does some sanity checks before doing any renaming (for instance would abort if you had both a series.S01E01.title x.mkv and Serias.S01E01.title.x.mkv)
  • doesn’t have the arbitrary command injection vulnerabilities of some variants of perl rename if you forget the -- (not supported by all) or ./ prefix.
  • It also works with non-ASCII file names (like for Ă©pisode capitalised to Épisode).
Answered By: Stéphane Chazelas

Just as with a sed or perl script, you can have more than one statement (either separated by ;, or by using multiple -e options) in a perl rename script. For example:

$ touch 'my.series.name.S01E'{01..03}'.title.of.the.episode.mkv'
$ ls -l
total 6
-rw-r--r-- 1 cas cas   0 Jun  7 10:56 my.series.name.S01E01.title.of.the.episode.mkv
-rw-r--r-- 1 cas cas   0 Jun  7 10:56 my.series.name.S01E02.title.of.the.episode.mkv
-rw-r--r-- 1 cas cas   0 Jun  7 10:56 my.series.name.S01E03.title.of.the.episode.mkv

$ rename 's/./ /g; s/ (mkv)$/.$1/; s/^(.*) (SddEdd) /[u$1] - $2 - /' *.mkv
$ ls -l
total 6
-rw-r--r-- 1 cas cas   0 Jun  7 10:57 [My series name] - S01E01 - title of the episode.mkv
-rw-r--r-- 1 cas cas   0 Jun  7 10:57 [My series name] - S01E02 - title of the episode.mkv
-rw-r--r-- 1 cas cas   0 Jun  7 10:57 [My series name] - S01E03 - title of the episode.mkv

The rename script:

  • Replaces all . characters with a space (including the . before the mkv extension)

  • Changes the space before the extension back to a .. This only works for .mkv files at present, but it’s easy enough to change this from s/ (mkv)$/.$1/ to, e.g. s/ (mkv|avi|mp4)$/.$1/ if you have videos in other container formats.

    If you needed to handle filenames with multiple "extensions", such as .xyz.mp4, that would be more work and it would be difficult to distinguish between words in the episode title and "extensions" unless the extensions were all known in advance.

  • Reformats the series name and episode-number portions of the filename as [Series] - SnnEnn - . The episode title and extension are left as they were after the previous two commands.

NOTE: if you write any other shell scripts working with these files, you will have to be careful to double-quote any variables containing the filenames as they all contain space, [, and ] characters. This isn’t a big deal as you should be quoting your variables anyway.

BTW, the statements in a rename script can be any valid perl code, and you can even use any perl module(s) you have installed. Not all of the statements have to directly change the filename (e.g. you can have code that increments a counter, or runs stat on the filename to check its size or timestamp, or whatever – even query a database or make a web API call to discover the episode’s title if it’s missing from the filename). Anything that changes $_ will change the filename.

Answered By: cas
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.