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
CodePudding user response:
$_.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.
- While it can be convenient to use
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"