1

I have a folder with a bunch of filenames I'd like to change. I'd like to insert a string after the 7th character.

Current filename:

  • 123456_9999999999999_DocName.pdf
  • 234567_9999999999999_DocName.pdf
  • 345678_9999999999999_DocName.pdf
  • 456789_9999999999999_DocName.pdf

Desired outcome:

  • 123456_DocType_9999999999999_DocName.pdf
  • 234567_DocType_9999999999999_DocName.pdf
  • 345678_DocType_9999999999999_DocName.pdf
  • 456789_DocType_9999999999999_DocName.pdf

I've tried the following I've pieced together, but it only renames one file and causes an error.

$location = 'C:\Users\username\Desktop\TEST' 
Get-ChildItem -Path $location -Recurse | 
    Rename-Item -NewName{$_.name -replace '(.*?)_(.*)', '_DocType_'}

How can I insert this string to multiple filenames after the 7th character please? Thanks for your help.

Olaf
  • 4,690
  • 2
  • 15
  • 23
JM1
  • 1,595
  • 5
  • 19
  • 41

4 Answers4

3

Note: The commands below focus just on the -replace operation; simply substituting the RHS of one of these operations for the RHS of the -replace operation in the code in the question is enough - although adding the -File switch is advisable to ensure that no accidental attempt to rename directories is made.[1]


To insert literally after the 7th character:

PS> '123456_9999999999999_DocName.pdf' -replace '(?<=^.{7})', 'DocType_'
123456_DocType_9999999999999_DocName.pdf

Note: As filimonic's helpful answer shows, you don't need a regex in this simple case; using the [string] type's .Insert() method is both simpler and faster.

More flexibly, to insert after the first _ in the name:

PS> '123456_9999999999999_DocName.pdf' -replace '(?<=^[^_]+_)', 'DocType_'
123456_DocType_9999999999999_DocName.pdf

Note:

  • (?<=...) is a positive look-behind assertion that matches the position of the subexpression represented by ... here - from the beginning (^), either the first 7 characters (.{7}), or a nonempty run (+) of characters other than _ ([^_]) followed by a _.

  • The replacement string ('DocType_') is therefore inserted into (a copy of) the original string at that position.

  • The look-behind approach obviates the need to refer to part of what was captured by the regex in the replacement operand, such as the use of $& in Wiktor's answer.

    • Note: While this approach is convenient and works well here, it has its pitfalls, because an anchored-to-the-start (^) subexpression inside a look-behind assertion doesn't always behave the same as outside of one - see this post.

See this answer for an overview of PowerShell's -replace operator.


[1] If the -replace operation turns out to be a no-op (if the regex doesn't match, the input string is returned as-is), trying to rename a directory to its existing name will actually generate an error, unlike with files - see GitHub issue #14903.

mklement0
  • 382,024
  • 64
  • 607
  • 775
3
'123456_9999999999999_DocName.pdf'.Insert(7,'DocType_')

No need any regular expressions. Works twice faster. Self-descriptive.

filimonic
  • 3,988
  • 2
  • 19
  • 26
1

You can use

$location = 'C:\Users\username\Desktop\TEST' 
Get-ChildItem -Path $location -Recurse | 
    Rename-Item -NewName{$_.name -replace '^.{6}', '$&_DocType'}

See the regex demo.

Details:

  • ^ - start of a string
  • .{6} - any 6 chars other than LF char.

$& in the replacement pattern is the reference to the whole match.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • @JM1 You are welcome. No need using lookbehinds that can be confusing even for some regex gold badge users. Please consider accepting the answer if it worked for you. – Wiktor Stribiżew Mar 02 '21 at 23:23
1

All answers submitted worked for me.

The final code I ended up using is:

Get-ChildItem -Path $location -Recurse | Rename-Item -NewName{$_.name.Insert(7,'DocType_')}

For me regex is a bit confusing and this code will make it easier for me and others on my team who may not use regex to support the code moving forward. I upvoted all answers as they work. Thanks to all who took time to help me and others who may have the same question.

JM1
  • 1,595
  • 5
  • 19
  • 41