2

I have a series of files that I want to rename. I want to rename the files with padded zeroes (total of 3 characters) after the first occurrence of the "-" delimiter like the examples below:

Old Name Example 1: 101-1_File1_Project1-000-END.txt
Desired New Name Example 1: 101-001_File1_Project1-000-END.txt

Old Name Example 2: 101-28_File1_Project1-000-END.txt
Desired New Name Example 2: 101-028_File1_Project1-000-END.txt

PowerShell code below works for Example 1 and not for Example 2. For Example 2 the result is:
101-0028_File1_Project1-000-END.txt

I thought this situation is exactly what PadRight is supposed to handle where there will be no more than 3 characters to the right of the "-". Can someone please assist?

PowerShell:

gci "C:\Path\Folder-with-Files" | ren -n {[regex]::replace($_.basename, '(?<=^[^-]*)-', {"$args".PadRight(3, '0')})+ $_.extension}
LBro
  • 113
  • 6

2 Answers2

4

There are two problems with your attempt:

  • Your regex doesn't actually capture the number to pad.

  • You're using .PadRight() instead of .PadLeft()

Therefore (note the \d+ in the regex):

'101-1_File1_Project1-000-END.txt', '101-28_File1_Project1-000-END.txt' | % {
  [regex]::Replace($_, '(?<=^[^-]+-)\d+', { $args[0].Value.PadLeft(3, '0') })
}

Note that in PowerShell (Core) you can now use the -replace operator with a script block:

'101-1_File1_Project1-000-END.txt', '101-28_File1_Project1-000-END.txt' | % {
  $_ -replace '(?<=^[^-]+-)\d+', { $_.Value.PadLeft(3, '0') }
}

Both commands output the following:

101-001_File1_Project1-000-END.txt
101-028_File1_Project1-000-END.txt
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    May I ask where `$args` gets populated? – Abraham Zinala Aug 28 '21 at 04:17
  • 2
    @AbrahamZinala `$args` here holds the regex match object data, it gets populated once the regex engine finds a match. You can read more about [this automatic variable here](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.1#args). – Wiktor Stribiżew Aug 28 '21 at 10:55
  • 1
    @AbrahamZinala, to add to Wiktor's comment: Specifically, the script block in the [`System.Text.RegularExpressions.Regex.Replace`](https://docs.microsoft.com/en-US/dotnet/api/System.Text.RegularExpressions.Regex.Replace) call acts as a [`MatchEvaluator`](https://docs.microsoft.com/en-US/dotnet/api/system.text.regularexpressions.matchevaluator) delegate, which has one parameter, namely the regex match at hand. In the absence of the script block formally declaring this parameter (e.g., `param($m)`), PowerShell makes it available via the regular unbound-arguments-array variable, `$args`. – mklement0 Aug 28 '21 at 12:34
0

How about using a loop and the format operator "-f" to achieve what you need? This should work actually:

Get-ChildItem -Path 'C:\Path\Folder-with-Files' |
ForEach-Object {
    If ($_.Name -match '(?<=^.*-)(\d+)(?=_)') {
        $Replace = '{0:00#}' -f ($Matches[1] -as [Int16])
        $NewName = $_.Name -replace '(?<=^.*-)(\d+)(?=_)', $Replace
        Rename-Item -Path $_.FullNAme -NewName $NewName
    }
}
Olaf
  • 4,690
  • 2
  • 15
  • 23