Search code examples
regexpowershellreplacefile-renamebatch-rename

PowerShell/Regex: Rename all files, modify number sequence


I currently have a directory with several thousand files in it.
Each file has _c04_ or _c05_ etc. somewhere within the filename.

I need to rename all of these files to _c004_ or _c005_ etc.
The only thing that needs to change, is adding the extra zero.
The letter will always be c.

I'm very new to regex, and I don't know the correct method for searching and replacing this string of characters.

I've written this code so far, but obviously this isn't scalable ...
(since it only modifies 04 rather than 01-99)

dir | rename-item -NewName {$_.name -replace "_c04_","_c004_"}

Solution

  • colsw's answer shows you how to use a capture group ((...)) in the regex and a reference to what it captured in the substitution string ($1) to perform the desired name transformation.

    • PowerShell uses the .NET Framework's regular-expression language, a brief introduction to which is available when your run Get-Help about_Regular_Expressions.
      Regular expressions are used not just with -replace, but also with the -match operator and in cmdlets such as Select-String.

    • The syntax that applies to the -replace operator specifically is covered in this answer.

    Your own solution attempt is preferable to a % (ForEach-Object)-based approach (which is shown in colsw's answer), because it involves only a single invocation of Rename-Item that uses a delay-bind script block to calculate the new name.

    Here is a working variation that uses zero-width lookbehind/ahead assertions to only match the position where the 0 should be inserted, without needing to repeat any part of the input string:

    Get-ChildItem | Rename-Item -NewName { $_.Name -replace '(?<=_c)(?=\d{2}_)', '0' } -WhatIf
    
    • Common parameter -WhatIf previews the changes; remove it to perform actual renaming.

    • As in colsw's answer, single quotes ('...') are used both for the regex and the substitution string, because "..." could create confusion with PowerShell's up-front string expansion (interpolation); e.g., "$1" would be interpreted as a variable reference.

      • If you do use "..." because you want to embed PowerShell variable values and expressions in the strings up front, be sure to escape $ instances that are meant for the regex engine as `$.

      • The alternative is to stick with single quotes and use the -f operator to embed a value; e.g. 'foo-{0}' -f $var

      • Either way, a pitfall is that any metacharacters embedded in variable values will have syntactic meaning, and to treat them verbatim requires a call to [regex]::Escape() for the regex operand, and doubling of any embedded $ characters for the substitution operand, so the best approach is to use something like:

        '...' -replace ('foo-{0}' -f [regex]::Escape($var1)),
                       ('bar-{0}' -f $var2.Replace('$', '$$'))
        
    • Note that any input files with names that result in no transformation will be left untouched; however, you can narrow down the set of files to process by using a wildcard pattern to preselect the files of interest:
      Get-ChildItem *_c[0-9][0-9]_* | Rename-Item ...