2

I am new using powershell and I need some help with a script.

I been using dir | Rename-Item -NewName {$_.Name -replace "Abc_123", "XYZ"} to change a bulk of file names in a folder but I would like to do more. I need to change from "Alex_Chris_123_456_ABC" to "AC_123_456_ABC".

I imagine that it goes like this.

Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("Abc_123", "XYZ")}

But this only changes specific characters. How can I add a conditional statement that will get the 1st character of strings before the first integer?

Jay
  • 21
  • 2
  • You could use `"Alex_Chris_123_456_ABC" -replace '^([a-z])[a-z]*_([a-z])[a-z]*','$1$2'`. – AdminOfThings Jan 07 '21 at 21:54
  • How predictable is your filename format? It could become complex to cover all scenarios like `[regex]::Replace("Alex_Chris_123_456_ABC",'(?i)^(?:[a-z]+_)+',{(-join ($args[0].Value -split '_' |% {[string]$_[0]}))+'_'})` works for 1 or more names but you need underscores separating the parts of the name. But `[regex]::Replace("Alex?Cross-123_456_ABC",'(?i)^(?:[a-z]+[^a-z])+',{(-join ($args[0].Value -split '[^a-z]' |% {[string]$_[0]}))+([string]$args[0])[-1]})` could work for any non-alpha separator. – AdminOfThings Jan 07 '21 at 22:17
  • Thank you for the replies. The file names are somewhat predictable since they are data files from our processing program. The names are separated by underscores. how can I included regex so it will read all files, folders and subfolders and rename them? – Jay Jan 07 '21 at 22:39

1 Answers1

1

You're not far off. With Rename-Item, you don't need a ForEach-Object loop in this case, because the NewName parameter can also take a scriptblock to perform an action on every file sent through the pipeline.

Try

$sourcePath = 'D:\Test'  # change this to the rootfolder where the files and subfolders are
Get-ChildItem -Path $sourcePath -File -Recurse | 
    Where-Object { $_.Name -match '^(\D+)(_.*)' } |  # see regex details below
    Rename-Item -NewName {
        # split on the underscore and join the first characters of each part together
        $prefix = ($matches[1].Split("_") | ForEach-Object { $_.Substring(0,1) }) -join ''
        '{0}{1}' -f $prefix, $matches[2]
    }

Regex details:

^             Assert position at the beginning of the string
(             Match the regular expression below and capture its match into backreference number 1
   \D         Match a single character that is not a digit 0..9
      +       Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)            
(             Match the regular expression below and capture its match into backreference number 2
   _          Match the character “_” literally
   .          Match any single character that is not a line break character
      *       Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
)
Theo
  • 57,719
  • 8
  • 24
  • 41