1

I am trying to rename a whole lot of files all located in one folder but in different subfolders. The files should be renamed so that their name consists of the foldername + the original file name. I am wondering if you could add a conditional statements so that the file name doesn't change if the file name already contains the folder name. The code below performs the function of renaming the files but doesn't contain the if statement.

dir -recurse | Rename-Item -NewName {$_.Directory.Name + " - " + $_.Name}

The code below is an example on how I imagine the code would look:

dir -recurse | if($_.Name -contains $_.Directory.Name) {Rename-Item -NewName {$_.Directory.Name + " - " + $_.Name}}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
DanielBeck
  • 81
  • 1
  • 9

3 Answers3

3

This should do it:

$rootFolder = 'D:\test'
Get-ChildItem -Path $rootFolder -File -Recurse | ForEach-Object {
    $folder = $_.Directory.Name
    if (!($_.Name.StartsWith($folder))) { $_ | Rename-Item -NewName ('{0} - {1}' -f $folder, $_.Name) }
}
Theo
  • 57,719
  • 8
  • 24
  • 41
2

Theo's answer works well, but there's an alternative that is both conceptually simpler and performs noticeably better:

You can take advantage of the fact that passing an unchanged file name to -NewName is a quiet no-op, so you can place all your logic in the -NewName script block:

Get-ChildItem -File -Recurse |
  Rename-Item -NewName { 
    if ($_.Name -like ($_.Directory.Name + ' - *') { # already renamed
      $_.Name # no change
    }
    else { # rename
      $_.Directory.Name ' - ' + $_.Name
    }
  } -WhatIf

-WhatIf previews the renaming operation; remove it to perform actual renaming.

Rather than using ForEach-Object call with a nested Rename-Item call in its script block - which means that Rename-Item is called once for each input file - this solution uses a single pipeline with a single Rename-Item invocation, where the new name (if changed) is determined via a delay-bind script block - see this answer for details.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

I was trying it in a way close to the way the question was asked. I wish I didn't have to add another foreach.

dir -recurse -file | & { 
  foreach ($i in $input) {
    if(-not ($i.Name.contains($i.Directory.Name))) {
      Rename-Item $i.fullname -NewName ($i.Directory.Name + ' - ' + $i.Name) -whatif 
    } 
  } 
}

Or like this

dir -recurse -file | % { 
  if(-not ($_.Name.contains($_.Directory.Name))) {
    Rename-Item $_.fullname -NewName ($_.Directory.Name + ' - ' + $_.Name) -whatif 
  } 
}
js2010
  • 23,033
  • 6
  • 64
  • 66