Renaming files by reformatting existing filenames

2019-01-04 14:09发布

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 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:

  1. Find the position of e??s?? (let's call it X).
  2. split the string at X-3.
  3. concatenate the string with a "_" in the middle.

4条回答
ら.Afraid
2楼-- · 2019-01-04 14:16

regex is the better solution, i propose other with formating number and use Template

$exampleoffile = @"
namev1_s01e01.mp4
namvev2_s03e02.mp4
VideoName_s20e15.mp4
VideoName_s15e1.mp4
VideoName_s1e16.avi
VideoName_s1e23.mv
"@

$template=@"
{file*:{prefix:Test1}_s{season:01}e{episode:01}{extension:.mp4}}
{file*:{prefix:1xxx}_s{season:100}e{episode:5}{extension:.mp4}}
{file*:{prefix:yyy3}_s{season:10}e{episode:02}{extension:.havi}}
"@



$exampleoffile | ConvertFrom-String -TemplateContent $template | 
% { "{0}_{1:D2}_e{2:D2}{3}" -f $_.file.prefix, [int]$_.file.season, [int]$_.file.episode, $_.file.extension }
查看更多
时光不老,我们不散
3楼-- · 2019-01-04 14:21

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 (finds all matches).

  • supports references to what the regular expression captured (and didn't capture) in the 2nd operand, <replace>, the replacement string (substitution string).

The "replacement language" for referencing regex captures supported in <replace> is itself not a regular expression - no matching happens there, only references to the results of the regex matching are supported.

Notably,

For convenience, here are the references supported in <replace> (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 number, where number is a decimal value, in the replacement string.

    • Annotations:

      • 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 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.

  • $` ... Includes all the text of the input string before the match in the replacement string.

  • $' ... Includes all 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 a PowerShell variable, you have three options:

    • Use "..." (expanding 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('$', '$$')
    • This relies on the [string]::Replace() method performing literal substring replacements.
      On a side note, this method is an alternative to -replace in simple cases, but note that it is case-sensitive by default.
    • Alternatively, use a nested -replace call, but the syntax is tricky due to the escaping requirements:
      ... -replace <search>, ($var -replace '\$', '$$$$')
查看更多
Viruses.
4楼-- · 2019-01-04 14:26

I highly recommend regex101.com for learning regex.

Something like this can work:

"VideoName_s01e01.mp4" -replace '(.*s)(\d+)(e.*)', '$1$2_$3'

Gives:

VideoName_s01_e01.mp4
查看更多
一纸荒年 Trace。
5楼-- · 2019-01-04 14:28

-replace is using , not . Thus you have to change the replace to:

-replace '_s([0-9]{1,2})e([0-9]{1,2})', '_s$1_e$2_'
查看更多
登录 后发表回答