Reputation: 29
I'm new to PowerShell, and have written the following script, using ISE, trying to rename a bunch of folders in the same directory, that strictly have the date format 20201130 into 2020-11-30 (simply adding dashes) the script kinda works, but I have 3 problems with it.
1- when it ran it threw a bunch of errors relating to Rename-Item
the error:
Rename-Item : Source and destination path must be different.
At line:13 char:25
+ ... ChildItem | Rename-Item -NewName { $_.Name -replace $file, $MyNewName ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (D:\Google Drive...Term 2\testings:String
) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.Renam
eItemCommand
2- when I tested it with a folder named (234567890) it still worked although the lenght of this folder is not eq to 8!
3- when the above renaming happened the resulting name was "1234-56-7890" which is strange since $MyNewName
should end with 2 characters! $file.Substring( 6 , 2 )
the script:
$FileNameArr_All = (Get-ChildItem).Name
$i = 0
foreach($file in $FileNameArr_All){
if ( ($file -match "^\d+$") -and ($file.Length -eq 8) ){
[string] $MyNewName = $file.Substring( 0 , 4 )+"-"+$file.Substring( 4 , 2 )+"-"+$file.Substring( 6 , 2 )
#the actual renamining of the item, where the error is:
Get-ChildItem | Rename-Item -NewName { $_.Name -replace $file, $MyNewName }
$message = "the item " + $file + " has been renamed : " + $MyNewName
Write-Output $message
$i ++
Write-Output $file.Length
}
}
if ($i -eq 0){
Write-Output "No mathcing items found."
}
else{
$message2 = "total of items affected: " + $i
Write-Output $message2
}
I know that's a lot but its my first post, and my first script, I appreciate helping with any of the points.
Many thanks :)
Upvotes: 2
Views: 152
Reputation: 439367
There's good information in the existing answers, but let me boil it down conceptually and offer a more streamlined solution:
You're looping over all directory names, individually, yet you're trying to rename all items in each loop iteration, due to using just Get-ChildItem
- no arguments - as input to Rename-Item
:
Get-ChildItem | Rename-Item ...
sends all files and subdirectories to Rename-Item
; you're effectively trying to rename all of them to their existing names (see next bullet point), which is a quiet no-op for files, but triggers the Source and destination path must be different
error you saw for directories.$MyNewName
is the complete new file name, so there is no reason to use -replace
on the original directory name: since $MyNewName
isn't a substring of the existing names, $_.Name -replace $file, $MyNewName
is effectively a no-op - the existing $_.Name
is passed through.
$MyNewName
- not inside a delay-bind script block ({ ... }
) - would be enough.However, you can streamline your solution to use a single pipeline, comprising a Get-ChildItem
call that uses a wildcard expression to match the directories of interest, and Rename-Item
with a delay-bind script block to insert the -
chars. in the names of the matching items via the regex-based -replace
operator:
# Create two sample directories
$null = New-Item -Force -Type Directory '12345678', '87654321'
Get-ChildItem -Directory -Path ('[0-9]' * 8) |
Rename-Item -NewName { $_.Name -replace '(.{4})(.{2})(.{2})', '$1-$2-$3' } -Verbose
The above performs the desired renaming and, due to use of the common -Verbose
parameter, prints the following (line breaks for readability):
VERBOSE: Performing the operation "Rename Directory" on target "Item: ...\12345678
Destination: ...\1234-56-78".
VERBOSE: Performing the operation "Rename Directory" on target "Item: ...\87654321
Destination: ...\8765-43-21".
Note:
If you want to preview the renaming operation, use the common -WhatIf
parameter with Rename-Item
.
If you need to know whether any files matched and were therefore renamed, capture the Get-ChildItem
call in a variable first:
$files = Get-ChildItem -Directory -Path ('[0-9]' * 8)
$files | Rename-Item -NewName { $_.Name -replace '(.{4})(.{2})(.{2})', '$1-$2-$3' } -Verbose
Write-Verbose -Verbose $(if ($files) { "$($files.Count) directories renamed." } else { 'No matching directories found.' })
Upvotes: 2
Reputation: 478
This script works a little bit more reliable, because it use the retun objects of "Get-ChildItem" and not only the file/folder names.
$i = 0
Get-ChildItem -Directory "C:\UserData\Test" | ForEach-Object {
if (($_.Name) -match "^\d{8}$") {
[System.String]$NewFolderName = ($_.Name).Insert(6, "-").Insert(4, "-")
Rename-Item -Path ($_.FullName) -NewName $NewFolderName
Write-Output ("the item {0} ({1}) has been renamed : {2}" -f ($_.Name), (($_.Name).Length), $NewFolderName)
$i++
}
}
if ($i) {
Write-Output ("total of items affected: {0}" -f $i)
} else{
Write-Output "No matching items found."
}
Upvotes: 0
Reputation: 61168
You can have the regex -match
in the Where-Object clause check the foldername for having exactly 8 digits and rename only those:
$i = 0
Get-ChildItem -Path 'D:\Test' -Directory |
Where-Object { $_.Name -match '^\d{8}$' } |
ForEach-Object {
$newName = $_.Name -replace '(\d{4})(\d{2})(\d{2})', '$1-$2-$3'
Write-Host "Renaming folder $($_.FullName) to: $newName"
$_ | Rename-Item -NewName $newName -WhatIf
$i++
}
if ($i) {
Write-Host "Total of items affected: $i"
}
else {
Write-Host "No matching folder names found"
}
Here, the -WhatIf
switch makes sure you only get to see what would happen. Nothing actually gets renamed yet. If you are satisfied with that output, remove the -WhatIf
switch from the Rename-Item cmdlet.
Also, the regex -replace
makes creating the new name format a lot easier than using Substring() multiple times, by capturing the digits in 3 diferent 'backreferences' and output them with dashed in between.
If you don't care about the Write-Host messages at any point, the code can be shortened to
Get-ChildItem -Path 'D:\Test' -Directory |
Where-Object { $_.Name -match '^\d{8}$' } |
Rename-Item -NewName {$_.Name -replace '(\d{4})(\d{2})(\d{2})', '$1-$2-$3'} -WhatIf
Upvotes: 1
Reputation: 478
instead of ...
Get-ChildItem | Rename-Item -NewName { $_.Name -replace $file, $MyNewName }
try ...
Rename-Item -Name $file -NewName $MyNewName
Upvotes: 1