I have a few video file like this:
VideoName_s01e01.mp4
where the season and episodes are variables. I want to add an underscore ("_") between the s??
and e??
.
I have been using powershell for renaming purposes, I have a starting point:
GCI $path -filter '*_s??e??*' -rec | Ren -new { $_.name -replace '_s[0-9][0-9]', '_s[0-9]_[0-9]_' } -passthru
This actually renamed my files VideoName_s[0-9]_e[0-9].mp4
.
Basically, I am looking for the characters s??e??
I just don't know how to make them variables in the replace section.
I think the best method would be:
e??s??
(let's call it X).X-3
._
" in the middle.Martin Brandl's answer provides an elegant and effective solution, but it's worth digging deeper:
PowerShell's -replace
operator (... -replace <search>[, <replace>]
):
Takes a regular expression as its first operand, <search>
(the search expression), and invariably matches globally, i.e., it replaces all matches.
'bar' -replace '[ra]', '@'
-> 'b@@'
\
-escape any regex metacharacters (e.g. .
) or use the result from a [regex]::Escape()
call:
'bar.none' -replace '\.', '-'
-> 'bar-none'
'bar.none' -replace [regex]::Escape('.'), '-'
-> 'bar-none'
Specifying a replacement expression, <replace>
, is optional, in which case the empty string is substituted for what <search>
matched, resulting in its effective removal.
'bar' -replace '[ra]'
-> 'b'
If <replace>
is specified, it supports two forms:
PowerShell (Core) 7 only: A script block ({ ... }
) as the replacement operand, which offers fully dynamic calculation of the replacement string on a per-match basis:
'Level 41' -replace '\d+', { 1 + $_.Value }
-> 'Level 42'
A string containing an expression that can reference what the regular expression captured (and didn't capture) - such as $&
to refer to what was matched - explained in detail below.
'bar' -replace '[ra]', '{$&}'
-> 'b{a}{r}'
-replace
matches case-insensitively (and can also be written as -ireplace
); to perform case-sensitive matching, use the form -creplace
.
The "replacement language" for referencing regex captures in a string-typed <replace>
operand is itself not a regular expression - no matching happens there, only references to the results of the regex matching are supported, via $
-prefixed placeholders that are not to be confused with PowerShell variables.
PowerShell's documentation (now) briefly explains the syntax of replacement strings in its conceptual about_Comparison_Operators
help topic.
For the full picture, refer to the Substitutions in Regular Expressions .NET framework help topic, which is applicable because PowerShell's -replace
uses the Regex.Replace()
method
behind the scenes.
For convenience, here are the references supported in the <replace>
string (excerpted from the page linked above, with emphasis and annotations added):
$number
(e.g., $1
) ... Includes the last substring matched by the capture group that is identified by the 1
-based number
in the replacement string:
Including (...)
, a parenthesized subexpression, in the regex implicitly creates a capture group (capturing group). By default, such capture groups are unnamed and must be referenced by their 1
-based (decimal) index reflecting the order in which they appear in the regex, so that $1
refers to what the 1st group in your regex captured, $2
to what the 2nd captured, ...
The form ${number}
(e.g., ${1}
) for disambiguation of the number is also supported (e.g., to make sure that $1
is recognized even if followed by, say, 000
, use ${1}000
).
Instead of relying on indices to refer to unnamed capture groups, you can name capture groups and refer to them by name - see next point.
If you're not interested in what the capture group matched, you can opt to ignore it by turning it into a non-capturing group with (?:...)
.
${name}
... Includes the last substring matched by the named capture group that is designated by (?<name>...)
in the replacement string.
$$
... Includes a single "$"
literal in the replacement string.
$&
... Includes a copy of the entire match in the replacement string ($0
works too, even though it isn't directly documented).
$`
... Includes the text of the input string before the match in the replacement string.
$'
... Includes the text of the input string after the match in the replacement string.
$+
... Includes the last group captured in the replacement string. [This relieves you of the need to know the last group's specific index.]
$_
... Includes the entire input string in the replacement string.
Finally, note that:
-replace
invariably matches globally, so if the input string contains multiple matches, the replacements above apply to each match.
It is generally preferable to use '...'
(single quotes) for both the regex and the replacement string, because single-quoted strings are non-expanding (non-interpolating), and therefore avoid confusion with PowerShell's own up-front expansions of $
-prefixed tokens and interpretation of `
chars.
If you do need to include PowerShell variables or expressions, you have three options:
Use "..."
(expandable strings) and `
-escape $
instances that are meant for the regex engine; e.g., `$1
in the following example:
'abc' -replace '(a)', "[`$1]-$HOME-"
which yields something like [a]-C:\Users\jdoe-bc
Build your string from literal pieces and variable references using string concatenation (+
); e.g.:
'abc' -replace '(a)', ('[$1]-' + $HOME + '-')
Use -f
, the string-formatting operator string concatenation; e.g.:
'abc' -replace '(a)', ('[$1]-{0}-' -f $HOME)
Given that you need to use $$
to escape a literal $
in the replacement string, use the following idiom to use a variable whose value you want to use literally:
... -replace <search>, $var.Replace('$', '$$')
[string]::Replace()
method performing literal substring replacements.-replace
in simple cases, but note that it is case-sensitive by default.-replace
operation, but the syntax is tricky due to the escaping requirements:... -replace <search>, ($var -replace '\$', '$$$$')