I'm trying to migrate a file server to One Drive the problem is that there are many folders and files with special characters like '|' , '<' etc..
And also for some reason that company used '*' for versioning like :
'*' => v.1
'**' => v.2
etc...
So I wrote a Powershell script that renames all the files and folders that include a special characters.
#Files
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace(":","-")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("<","-")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace(">","-")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("?","-")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("/","-")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("|","-")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("**********","10")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*********","9")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("********","8")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*******","7")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("******","6")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*****","5")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("****","4")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("***","3")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("**","2")}
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*","1")}
#Directories
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace(":","-")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("<","-")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace(">","-")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("?","-")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("/","-")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("|","-")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("**********","10")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*********","9")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("********","8")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*******","7")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("******","6")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*****","5")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("****","4")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("***","3")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("**","2")}
Get-ChildItem -Directory -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*","1")}
So this works fine but for some reason I'm getting many error when renaming the folders even though it worked...
Error:
… curse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace("*", …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Source and destination path must be different.
I also tried what this thread suggest but this didn't work for me:
Does anyone have a clue why I'm getting this error? And How to fix it?
Thanks in advance.
The error is normal, if you dont have reasons to rename the directory or file because the filename is ok and no character to modify, the name stays same, so an error is displayed saying you cant rename a file/directory with same name.
Despite this error, the process is not stopped and has no impact on the final result
i suggest you to set all couple key to search , new value in an object:
#add your template to replace
$r = #{
":" = "-"
"<" = "-"
"**********" = "10";
"*********" = "9";
"********" = "8"
}
$Folders=Get-ChildItem -Directory -path -Recurse
foreach($folder in $Folders){
$newname = $folder.Name
foreach($k in $r.Keys){
$newname = $newname.Replace($k,$r.$k)
}
#rename only if original name and newname are differents
if(!$newname.equals($folder.Name)){
Rename-Item -Path $folder.FullName -NewName $newname
}
}
do samething for your files......
On my Windows machine I cannot have characters like that in a file or folder name, but theoretically this should work:
(Get-ChildItem -File) | ForEach-Object {
$_ | Rename-Item -NewName {
# replace the '*' by the number of consecutive asterikses found
$name = $_.Name
$match = ([regex] '(\*+)').Match($name)
while ($match.Success) {
$name = $name -replace [regex]::Escape($match), $match.Length
$match = $match.NextMatch()
}
# finally replace the other special characters by a hyphen
$name -replace '[:<>?/|]', '-'
}
}
the brackets around Get-ChildItem force it to complete the gathering before piping to rename. Otherwise, you may end up trying to rename items that were already processed
You run 10 lines of code to rename only 1 type of match per line but effectively you run the same loop 10 times, in this case your code try to rename all folders every single time, but it errors out when it tries to rename the folder with the same newName , because Rename-Item tries to rename it even if the new name is the same as the old one, it appears to not have a validator for that.
In that case, the "cheapest" solution is to add -ErrorAction SilentlyContinue . But a more sophisticated solution would be to validate your dataset with an if statement or a more appropriate for the case switch .
Update : Regex might not be the easiest thing to deal with, so the example bellow account for the wildcard * character as well.
$allFolders=Get-ChildItem -Directory -Recurse
foreach($folder in $allFolders){
switch -Regex ($folder.Name){
'[:<>?\|]'{
$newName=$folder.Name.replace("[:<>?\|]","-")
Rename-Item -Path $folder.FullName -NewName $newName
}
'\*'{
$regexFormat = '\*'
$matchNumber=[regex]::Matches($folder.Name, $regexFormat) | ForEach-Object { $_.value }
$newName=$folder.Name.replace("*",$matchNumber.Count)
Rename-Item -Path $folder.FullName -NewName $newName
}
}
}
This way, you will run 1 loop instead of 10 and you only try to rename items that matches the pattern you provide, so you will see a significant performance increase compared to the multiline code you are using.
Related
How could I modify name of every file by adding _ before the filename extension in a Get-ChildItem -Include without calling 3 times a Foreach.
My script works, but I would like to simplify it by using -Include and not -Filter.
function renommer_fichier {
$files = Get-ChildItem -Path C:\mestp\mesFichiers -Filter *.jpg
Foreach ($file in $files)
{
$file | Rename-Item -NewName { $_.Name -replace '.jpg', '_.jpg' }
}
$files = Get-ChildItem -Path C:\mestp\mesFichiers -Filter *.mp3
Foreach ($file in $files)
{
$file | Rename-Item -NewName { $_.Name -replace '.mp3', '_.mp3' }
}
$files = Get-ChildItem -Path C:\mestp\mesFichiers -Filter *.mpeg
Foreach ($file in $files)
{
$file | Rename-Item -NewName { $_.Name -replace '.mpeg', '_.mpeg' }
}
}
Unfortunately, the -Include parameter - which supports specifying multiple patterns - doesn't work as one would intuitively expect:
Use Get-Item instead of Get-ChildItem and append \* to the input path to make -Include work as intended.
See this answer for background information.
Use the .BaseName and .Extension properties in the delay-bind script block you're passing to Rename-Item to facilitate inserting _ before the filename extension.
Get-Item -Path C:\mestp\mesFichiers\* -Include *.jpg, *mp3, *.mpeg |
Rename-Item -WhatIf -NewName { $_.BaseName + '_' + $_.Extension }
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
I have a lot of files in a directory containing square brackets for example:
Filename 1 [12454365].txt
I tried the following script but it's giving me the an error.
get-childitem -recurse | foreach { move-item -literalpath $_.name ($_.name -replace '\[.*\]', '')}
Error message
move-item : A device attached to the system is not functioning.
Only want to remove square brackets not everything in between!
If you specify -Recurse, you will need to specify the file with FullName because it will be targeted other than the current directory.
(Get-ChildItem -File -Recurse) | foreach {
$dest = Join-Path $_.DirectoryName ($_.Name -replace "[\[\]]")
Move-Item -LiteralPath $_.FullName $dest
}
Also, it is better to use Rename-Item for file renaming.
(Get-ChildItem -File -Recurse) | Rename-Item -NewName { $_.Name -replace "[\[\]]" }
I believe the issue is that you are replacing the brackets and everything in between.
Get-ChildItem * -Filter "*`[*`]*" | Rename-Item -NewName { $_.name -replace '\[','' -replace '\]','' }
Renaming pdf-files with the command
get-childitem | % { rename-item $_ "2017-$_"}
renames correctly 33 files but applying the same command to more than 33 files produces filenames looking like 2017-2017-2017...-original.name.
How can I surmount that limitation for which I have not found any explication?
Since Rename-Item accepts piped input there is no need for a ForEach,
to exclude files beginning with 2017- :
Get-ChildItem *.pdf -exclude 2017-*.pdf |
Rename-Item -Newname { $_.Name -replace '^','2017-' } -whatif
to exclude any year/4digit-number prefix :
Get-ChildItem *.pdf |
Where-Object Name -notmatch '^\d{4}-' |
Rename-Item -Newname { $_.Name -replace '^','2017-' } -whatif
If the output looks OK, remove the -whatif
Looks like your code is chasing it's tail (i.e. it's renaming things that have already been renamed). Capture the list of files into a variable, then loop through that, renaming the captured list of files:
$filesToRename = Get-ChildItem -Filter '*.pdf'
$filesToRename | ForEach-Object {
Rename-Item -Path $_.FullName -NewName "2017-$($_.FullName)"
}
If you want to exclude already renamed files:
$prefix = '2017-'
$filesToRename = Get-ChildItem -Filter '*.pdf' | Where-Object { !$_.Name.StartsWith($prefix) }
$filesToRename | ForEach-Object {
Rename-Item -Path $_.FullName -NewName "$prefix$($_.FullName)"
}
I have to go through many levels of child folders and remove special characters that are invalid in SharePoint, mainly '#&'
I have scoured the internet trying different commands; rename-item/move-item, variations of the two, all to no avail. The closest i've gotten is using:
Get-ChildItem -Recurse | Rename-Item -NewName {$_.Name -replace'[!##&]','_'}
but i keep getting this error: Rename-item: Source and destination path must be different.
Could someone point me in the right direction?
Regards
That error only happens when you attempt to rename a directory to the same NewName as the current name, you can safely ignore it.
Add -ErrorAction SilentlyContinue to silently suppress the error message:
Get-ChildItem -Recurse | Rename-Item -NewName {$_.Name -replace'[!##&]','_'} -ErrorAction SilentlyContinue
You need to filter out the files that you're not planning to rename:
Get-ChildItem -Recurse |
Where-Object { $_.Name -match '[!##&]' } |
Rename-Item -NewName {$_.Name -replace '[!##&]','_'}
something like this may work
dir -Recurse -File | ? basename -Match '[!##&]' | % {
# if the file.txt already exists, rename it to file-1.txt and so on
$num = 1
$base = $_.basename -replace'[!##&]', '_'
$ext = $_.extension
$destdir = Split-Path $_.FullName
$newname = Join-Path $destdir "$base$ext"
while (Test-Path $newname) {
$newname = Join-Path $destdir "$base-$num$ext"
$num++
}
ren $_.fullname $newname
}
I'm attempting to do some specific operations with the names of folders and files in a set directory.
I'm replacing underscores ("_") with nothing ("")
I'm replacing spaces (" ") with nothing ("")
I'm replacing lowercase hp ("hp") with uppercase hp and a space ("HP
")
I'm replacing uppercase hp ("HP") with uppercase hp and a space ("HP
")
I'm replacing camelcase hp ("Hp") with uppercase hp and a space ("HP
")
I'm replacing the lowercase letter a ("a") with the uppercase letter
a ("A") ...B...C etc.
This is my powershell script:
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("_","") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace(" ","") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("hp","HP ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("HP","HP ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("Hp","HP ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("&"," & ") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("a","A") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("b","B") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("c","C") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("d","D") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("e","E") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("f","F") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("g","G") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("h","H") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("i","I") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("j","J") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("k","K") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("l","L") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("m","M") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("n","N") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("o","O") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("p","P") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("q","Q") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("r","R") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("s","S") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("t","T") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("u","U") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("v","V") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("w","W") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("x","X") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("y","Y") }
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace("z","Z") }
I know, it's essentially a bunch of loops.
I had a couple of problems and questions:
The above code does not work for replacing lowercase letters with uppercase letters (a-z), why is that? It also seems to not work all the time recursively either. E.G folder name HP 14-Z040wm, file inside is HP14-Z040WM
How can I ultimately make this script better? As in shorter, faster etc.
(I tagged batch-file as well, as I would definitely accept (my first choice when I started making this) a batch file solution.
First, only get items once, then process them once. Next, you can replace the character substitution with the ToUpper() method. Third, since all those methods return strings, you can just chain the methods together.
It's easy enough for files:
Get-ChildItem -Recurse -File | ForEach-Object {
$NewName = $_.Name.ToUpper().Replace('_','').Replace(' ','').Replace('HP', 'HP ');
Rename-Item -Path $_.FullName -NewName $NewName;
}
Folders, however, are a whole other ball of wax. You can't rename a folder path to the same name, and path names are case-insensitive. So neither Rename-Item nor Move-Item will work alone. You'll have to use a temporary folder name.
Here's code for both files and folders:
#Unique name for temp folder
$TempFolderName = [System.Guid]::NewGuid().Guid;
Get-ChildItem -Recurse | ForEach-Object {
$NewName = $_.Name.ToUpper().Replace('_','').Replace(' ','').Replace('HP', 'HP ');
if (!$_.PSIsContainer) {
#Regular file
Rename-Item -Path $_.FullName -NewName $NewName;
}
else {
#Get the parent folder
$ParentFolder = Split-Path $_.FullName;
#Rename folder to temp name
Rename-Item -Path $_.FullName -NewName (Join-Path $ParentFolder $TempFolderName);
#Rename temp name to folder
Rename-Item -Path (Join-Path $ParentFolder $TempFolderName) -NewName (Join-Path $ParentFolder $NewName);
}
}
You should rename files first then folders. Trying to do both at the same time could invalidate other objects in the pipeline and get you errors. Also the repetition here can be saved with -replace "\s|_" and.ToUpper().-replace` is a regex function that we are using to replace all space and underscores with nothing. You can chain these as well so we are also going to replace HP at the start of the name and add in the space.
$path = "C:\Temp"
Get-ChildItem $path -Recurse -Directory | Rename-Item -NewName {($_.Name).ToUpper() -replace "\s|_"} -WhatIf
Get-ChildItem $path -Recurse -File | Rename-Item -NewName {($_.Name).ToUpper() -replace "\s|_"} -WhatIf
I have put -WhatIf on there for testing. If this code is only going to change the case of the entity names then it is going to give an error that the file/folder name needs to be different (folders in are case insensitive). So you would need to move it to a temporary folder first. Windows is not case sensitive so it will see you trying to name the object the same.
You need to be very careful running this code as it permanently changes folder names to temporary names. If the process fails you would have to manually fix the broken folders. Look for .bak folders in that case. My tests did work however so hopefully it is just a precaution. Run your own tests to be sure.
$path = "C:\Temp"
# Rename folders
Get-ChildItem $path -Recurse -Directory | ForEach-Object{
# Save the current name
$originalName = $_.Name
# Make sure the temp name is unique.
$temp = Rename-Item -Path $_.FullName -NewName "$originalName.bak" -PassThru
# Rename using the saved name and proposed changes.
$temp | Rename-Item -NewName ($originalName.ToUpper() -replace "\s|_" -replace "^HP","HP ")
}
Get-ChildItem $path -Recurse -File | Rename-Item -NewName {$_.Name.ToUpper() -replace "\s|_" -replace "^HP","HP "}