1

I'm rather new to powershell, and I have been trying to make a script that takes all files in a directory with numbers in the title and renames them according to a template.

My Script looks like such:

$NewObjName = "My New Name"

cd E:\Test

dir | ren -NewName {$_.name -replace '([a-zA-Z])([a-zA-Z])\.(\d?)(\d).*','0$3$4'}
dir | ren -NewName {$_.name -replace '(\d?)(\d)(\d)',{Part $2$3 $NewObjName.txt}}

and does this:

PS E:\Test> ls


    Directory: E:\Test


Mode                 LastWriteTime         Length Name                                                                                                                                            
----                 -------------         ------ ----                                                                                                                                            
-a----          8/1/2022   2:14 PM              0 AB.01CDE                                                                                                                                        
-a----          8/1/2022   2:15 PM              0 AB.02CDE                                                                                                                                        
-a----          8/1/2022   2:15 PM              0 AB.10CDE                                                                                                                                        
-a----          8/1/2022   2:15 PM              0 AB.11CDE                                                                                                                                        



PS E:\Test> E:\RenameScript.ps1

PS E:\Test> ls


    Directory: E:\Test


Mode                 LastWriteTime         Length Name                                                                                                                                            
----                 -------------         ------ ----                                                                                                                                            
-a----          8/1/2022   2:14 PM              0 Part 01 $NewObjName.txt                                                                                                                         
-a----          8/1/2022   2:15 PM              0 Part 02 $NewObjName.txt                                                                                                                         
-a----          8/1/2022   2:15 PM              0 Part 10 $NewObjName.txt                                                                                                                         
-a----          8/1/2022   2:15 PM              0 Part 11 $NewObjName.txt                                                                                                                         



PS E:\Test> 

So it just puts in the variable name instead of the value of the variable. I'm not sure if -replace doesn't allow variables other than regex groupings or if I'm just missing something?

Thanks in advance :)

Edit: My expected result would be something like:

PS E:\Test> ls


    Directory: E:\Test


Mode                 LastWriteTime         Length Name                                                                                                                                            
----                 -------------         ------ ----                                                                                                                                            
-a----          8/1/2022   2:14 PM              0 Part 01 My New Name.txt                                                                                                                         
-a----          8/1/2022   2:15 PM              0 Part 02 My New Name.txt                                                                                                                         
-a----          8/1/2022   2:15 PM              0 Part 10 My New Name.txt                                                                                                                         
-a----          8/1/2022   2:15 PM              0 Part 11 My New Name.txt    
Loger42
  • 13
  • 3
  • 1
    Perhaps you should show what you’re trying to end up with. – Doug Maurer Aug 01 '22 at 19:39
  • 1
    Please show the expected result, and use double-quotes as well: `'(\d?)(\d)(\d)',"Part $2$3 $NewObjName.txt"`. – Abraham Zinala Aug 01 '22 at 19:41
  • I added the expected results, also I tried changing to double quotes and now I'm not really sure what it's doing tbh. This is my first powershell project so I'm quite unfamiliar with the workings of the language. – Loger42 Aug 01 '22 at 20:01

1 Answers1

1

$_.name -replace '(\d?)(\d)(\d)',{Part $2$3 $NewObjName.txt}

  • { ... } is a script-block literal, not a string literal.

    • When a script block is used in a context where it is coerced to a string, such as in your case, its verbatim content (sans { and }) is used. Therefore, $NewObjName was not expanded (interpolated).

      • While it can be convenient to use { ... } in lieu of an actual string literal in order to avoid the need for escaping of embedded quoting, it is best avoided in the interest of conceptual clarity.
    • As an aside: In PowerShell (Core) 6.1+, script blocks are now supported as the replacement operand of the -replace operator, albeit as script blocks, i.e. they provide a piece of code to run for a each match in order to dynamically determine the replacement text - see this answer for an example.

  • Since you need string expansion (interpolation), you must specify your replacement operand as an expandable (double-quoted) string ("...")

    • However, $2 and $3 - even tough they look like PowerShell variables - are actually placeholders for what the regex operation matched, and therefore must be passed through to the .NET regex engine.

    • To that end (to prevent up-front expansion by PowerShell), they must be escaped as `$2 and `$3, using `, the so-called backtick, PowerShell's escape character.

    • Note:

      • If you use a verbatim (single-quoted) string ('...') - which is the best choice if interpolation is not needed - placeholders such as $2 and $3 can be used as-is.

      • As an alternative to the expandable string solution below, you can conceptually separate the aspect of up-front expansion and the verbatim string to pass to the .NET regex engine via -f, the format operator:

        # Same as: "Part `$2`$3 $NewObjName.txt"
        ('Part $2$3 {0}.txt' -f $NewObjName)
        

Therefore:

$_.name -replace '(\d?)(\d)(\d)', "Part `$2`$3 $NewObjName.txt"
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Ah, I did not know that $2 and $3 needed to be escaped! Thank you for your assistance. This answered my question perfectly, and helped me learn several new things! :) – Loger42 Aug 01 '22 at 20:11
  • Glad to hear it, @Loger42; my pleasure. – mklement0 Aug 01 '22 at 20:12
  • Only need to be escaped in double quotes, single quotes they are fine – Doug Maurer Aug 01 '22 at 20:39
  • @DougMaurer good point in general, I've added it to the answer. Note that in this particular case double quotes are a must due to the need to expand `$NewObjectName` - if you want to use just a string literal, that is. I've also added an alternative with `-f`. – mklement0 Aug 01 '22 at 20:59
  • @Loger42, please also see the update to the answer. – mklement0 Aug 01 '22 at 21:00