I have a script that I use to clean up file names to match my preferred style/format. It works as is, but its kinda ugly and does a loop for each change. Ideally it would process all the changes for each file name at once and then proceed to the next, rather then grabbing the gci every time.
Any suggestions on making this a bit more elegant/streamlined? Thanks!
#need this for camel casing later
$Culture = Get-Culture
#Remove Other Tags
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('(retail)','') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('(epub)','') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('(mobi)','') }
#Pad Dashes
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('-',' - ') }
#Replace Underscores with Spaces
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('_',' ') }
#Replace dots with space except for in the extension
Get-ChildItem -Recurse | Rename-Item -NewName { ($_.BaseName -replace '\.',' ') + $_.Extension }
#Rename to Camel Casecd
Get-ChildItem -recurse | Rename-Item -NewName {$Culture.textinfo.totitlecase($_.FullName)}
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace(' .','.') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('.Epub','.epub') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('.Pdf','.pdf') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('.Mp3','.mp3') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('For','for') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('And','and') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('In','in') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('On','on') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('Of','of') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('From','from') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('To','to') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace(' A ',' a ') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace(' i',' I') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('epubo O','epub') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('Html','HTML') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('Css','CSS') }
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace('Css','CSS') }
#Remove Double Spaces
Get-ChildItem -Recurse | Rename-Item -NewName { $_.Name.Replace(' ',' ') }
Here is a proposal if you think it is elegant :
$hash = #{'(retail)' = ''; '(epub)' = ''; '(mobi)' = ''; 'And' = 'and' } #To be completed
$Culture = Get-Culture
$items = Get-ChildItem -Recurse
foreach ($item in $items) {
$name = $item.Name
foreach ($h in $hash.Keys) {
$name = $name.Replace($h, $hash.$h)
}
Rename-Item -Path $item.FullName -NewName $name
}
I know it is probably not complete but it might be an inspiration for further improvements ;-)
Get-ChildItem -Recurse -Directory |
ForEach-Object {
$NewBaseName = $_.BaseName -replace '\(epub\)' -replace '\(mobi\)' -replace '\(retail\)'
$NewBaseName = $NewBaseName -replace '-', ' - ' -replace '_', ' ' -replace '\.', ' ' -replace '\s+', ' '
$NewBaseName = $NewBaseName.toLower() -replace 'epubo O', 'epub' -creplace 'html', 'HTML' -creplace 'css', 'CSS'
$NewExtension = $_.Extension.toLower()
$NewName = Join-Path -Path $_.Directory -ChildPath ($NewBaseName + $NewExtension)
Rename-Item -Path $_.FullName -NewName $NewName
}
This way you traverse the directories only once. That should at least be faster than your approach.
Related
I have some files such as:
20220716_165615-IMG_1234.jpg
20220717_102742-IMG_1235.jpg
20220717_193212-IMG_1236.jpg
They need to be renamed to:
2022-07-16_16.56.15-IMG_1234.jpg
2022-07-17_10.27.42-IMG_1235.jpg
2022-07-17_19.32.12-IMG_1236.jpg
I tried using a rename in Windows 10 CMD:
ren ????????_??????-*.* ????-??-??_??.??.??-*.*
But I was not successful.
Turns out it's easier with windows powershell:
for the current folder:
Get-Item .\*.* | Rename-Item -NewName {$_.BaseName.insert(15,'.') + $_.Extension} -WhatIf
Get-Item .\*.* | Rename-Item -NewName {$_.BaseName.insert(13,'.') + $_.Extension} -WhatIf
Get-Item .\*.* | Rename-Item -NewName {$_.BaseName.insert(6,'-') + $_.Extension} -WhatIf
Get-Item .\*.* | Rename-Item -NewName {$_.BaseName.insert(4,'-') + $_.Extension} -WhatIf
for the contents of the subfolders:
Get-ChildItem -File -Recurse | Rename-Item -NewName {$_.BaseName.insert(11,'.') + $_.Extension} -WhatIf
Get-ChildItem -File -Recurse | Rename-Item -NewName {$_.BaseName.insert(6,'-') + $_.Extension} -WhatIf
Get-ChildItem -File -Recurse | Rename-Item -NewName {$_.BaseName.insert(4,'-') + $_.Extension} -WhatIf
(the -WhatIf argument shows what's about to be done. To execute it for real, remove the -WhatIf argument)
I'm trying to write a PowerShell script that goes into every folder in a certain directory, and every child with the parent.
Example:
Z:\Folder1\File.txt
Z:\Folder1\Picture.jpeg
Z:\Folder1\Data.csv
Z:\Folder2\File.txt
Z:\Folder2\Picture.jpeg
Z:\Folder2\Data.csv
=
Z:\Folder1\Folder1.txt
Z:\Folder1\Folder1.jpeg
Z:\Folder1\Folder1.csv
Z:\Folder2\Folder2.txt
Z:\Folder2\Folder2.jpeg
Z:\Folder2\Folder2.csv
I have this so far
dir | rename-item -NewName {$_.name -replace *,"Folder1"}
Please let me know how I can loop through every folder, and how I can rename them based on the folder name.
Thanks!
This should do what you're asking:
Get-ChildItem Z:\Folder* -Directory | Get-ChildItem -File | Rename-Item -NewName { $_.DirectoryName.split('\')[-1] + $_.Extension } -WhatIf
Adjust the folder name filter (.\Folder*) as required and remove the -WhatIf if you're satisfied with the changes it will make.
Obviously this doesn't account for where you might have a name clash if there are two files in the folder with the same extension. The following would do that:
$FilesToRename = Get-ChildItem Z:\Folder* -Directory | Get-ChildItem -File
foreach ($FileToRename in $FilesToRename) {
$NewName = $FileToRename.DirectoryName.split('\')[-1] + $FileToRename.Extension
$NewFile = Join-Path $FileToRename.Directory $NewName
$i = 0
While (Test-Path $NewFile) {
$i = $i++
$NewName = $FileToRename.DirectoryName.split('\')[-1] + "[$i]" + $FileToRename.Extension
$NewFile = Join-Path $FileToRename.Directory $NewName
}
$FileToRename | Rename-Item -NewName $NewName
}
The following code works beautifully:
Get-ChildItem -Path E:\Videos\Movies\ -File -Include *.avi,*.mp4,*.mkv -Recurse | Rename-Item -NewName { $_.Name.replace("DVDRIP","DVD") }
Now, 2 questions:
Anyway I can input multiple words to replace to DVD? Here is what I was thinking:
Get-ChildItem -Path E:\Videos\Movies\ -File -Include *.avi,*.mp4,*.mkv -Recurse | Rename-Item -NewName { $_.Name.replace("DVDRIP|DVDR|DVD5|DVD9","DVD") }
I have tried this and other variations, but I just can't figure it out.
How can I make the code NOT case sensitive so I don't have multiple lines trying to catch the possible different spellings?
You are using a regex-pattern with a normal text-replace method. Try -replace which is made for regex. -replace is also case-insensitive by default.
Get-ChildItem -Path E:\Videos\Movies\ -File -Include *.avi,*.mp4,*.mkv -Recurse |
Rename-Item -NewName { $_.Name -replace "DVDRIP|DVDR|DVD5|DVD9","DVD" }
I am trying to use Rename-Item to remove trailing characters including the hyphen from a filename, ex. 123456.001.zip-4.22815.ren to 123456-001.zip.
Rename-Item -NewName ($_.Name.split('-')[0])
seems to be something I am missing after the split.
The split operation must be performed in a scriptblock ({}). A simple expression (()) won't work.
... | Rename-Item -NewName { $_.Name.Split('-')[0] }
Add -replace '^(\d+)\.', '$1-' if you want the period replaced with a hyphen.
... | Rename-Item -NewName { $_.Name.Split('-')[0] -replace '^(\d+)\.', '$1-' }
I got my script to work with these changes;
$src = "D:\temp"
Get-ChildItem -path $src -filter *.ren | ForEach-Object {
Rename-item -path $_.FullName -newname $_.Name.Split('-')[0] }
With PowerShell (although other suggestions are welcome), how does one recursively loop a directory/folder and
replace text A with B in all files,
rename all files so that A is replaced by B, and last
rename all folders also so that A is replaced by B?
With a few requirements refinements, I ended up with this script:
$match = "MyAssembly"
$replacement = Read-Host "Please enter a solution name"
$files = Get-ChildItem $(get-location) -filter *MyAssembly* -Recurse
$files |
Sort-Object -Descending -Property { $_.FullName } |
Rename-Item -newname { $_.name -replace $match, $replacement } -force
$files = Get-ChildItem $(get-location) -include *.cs, *.csproj, *.sln -Recurse
foreach($file in $files)
{
((Get-Content $file.fullname) -creplace $match, $replacement) | set-content $file.fullname
}
read-host -prompt "Done! Press any key to close."
I would go with something like this:
Get-ChildItem $directory -Recurse |
Sort-Object -Descending -Property { $_.FullName } |
ForEach-Object {
if (!$_.PsIsContainer) {
($_|Get-Content) -replace 'A', 'B' | Set-Content $_.FullName
}
$_
} |
Rename-Item { $_.name -replace 'A', 'B' }
The Sort-Object is there to ensure that first children (subdirs, files) are listed and then directories after them. (12345)
Untested, but should give you a starting point:
$a = 'A';
$b = 'B';
$all = ls -recurse;
$files = = $all | where{ !$_.PSIsContainer );
$files | %{
$c = ( $_ | get-itemcontent -replace $a,$b );
$c | out-file $_;
}
$all | rename-item -newname ( $_.Name -replace $a,$b );
Untested, may be I'm more lucky ;-)
$hello = 'hello'
$world = 'world'
$files = ls -recurse | ? {-not $_.PSIsContainer}
foearch ($file in $files) {
gc -path $file | % {$_ -replace $hello, $world} | Set-Content $file
ri -newname ($file.name -replace $hello, $world)
}
ls -recurse | ? {$_.PSIsContainer} | ri -newname ($_.name -replace $hello, $world)
To use the same recursion:
$hello = 'hello'
$world = 'world'
$everything = ls -recurse
foearch ($thing in $everything) {
if ($thing.PSIsContainer -ne $true) {
gc -path $thing | % {$_ -replace $hello, $world} | Set-Content $thing
}
ri -newname ($thing.name -replace $hello, $world)
}
I needed this for myself and below slightly better version of the script.
I added followings:
Support for verbose parameter so you can actually see what changes script has made.
Ability to specify folders so you can limit changes.
Adding bower.json, txt and md in to include extensions.
Search and replace content first, do rename later.
Do not replace content if search string is not found (this avoids unnecessary change in modified date).
[CmdletBinding(SupportsShouldProcess=$true)]
Param()
$match = "MyProject"
$replacement = Read-Host "Please enter project name"
$searchFolders = #("MyProject.JS", "MyProject.WebApi", ".")
$extensions = #("*.cs", "*.csproj", "*.sln", "bower.json", "*.txt", "*.md")
foreach($searchFolderRelative in $searchFolders)
{
$searchFolder = join-path (get-location) $searchFolderRelative
Write-Verbose "Folder: $searchFolder"
$recurse = $searchFolderRelative -ne "."
if (test-path $searchFolder)
{
$files = Get-ChildItem (join-path $searchFolder "*") -file -include $extensions -Recurse:$recurse |
Where-Object {Select-String -Path $_.FullName $match -SimpleMatch -Quiet}
foreach($file in $files)
{
Write-Verbose "Replaced $match in $file"
((Get-Content $file.fullname) -creplace $match, $replacement) | set-content $file.fullname
}
$files = Get-ChildItem $searchFolder -filter *$match* -Recurse:$recurse
$files |
Sort-Object -Descending -Property { $_.FullName } |
% {
Write-Verbose "Renamed $_"
$newName = $_.name -replace $match, $replacement
Rename-Item $_.FullName -newname $newName -force
}
}
else
{
Write-Warning "Path not found: $searchFolder"
}
}
Note that one change from the answer is that above recurses folder only in specified folders, not in root. If you don't want that then just set $recurse = true.