問題描述
一個驅動器遷移 (One Drive Migration)
我正在嘗試將文件服務器遷移到 One Drive,問題是有許多文件夾和文件帶有“|”等特殊字符 , '<' 等等。
而且出於某種原因,該公司使用'*'進行版本控制,例如:
'*' => v.1 '**' => v.2
etc...
所以我寫了一個 Powershell 腳本來重命名所有包含特殊字符的文件和文件夾。
#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")}
所以這個工作正常,但由於某種原因,我在重命名文件夾時遇到了很多錯誤,即使它工作正常......
錯誤:
… curse | % { Rename‑Item ‑Path $_.PSPath ‑NewName $_.Name.replace("*", …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Source and destination path must be different.
我也試過這個線程 建議但這沒有 不適合我:
有人知道我為什麼會收到此錯誤嗎?以及如何解決?
提前致謝。
參考解法
方法 1:
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......
方法 2:
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 '[:<>?/|]', '‑'
}
}
</code></pre>
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
方法 3:
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.
(by Wilson Silva、Frenchy、Theo、Vasil Nikolov)
參考文件