Vicky Dev
Vicky Dev

Reputation: 2193

Rename files sequentially and if file already exists (name conflict) then just increment the iterator to give the file a fresh name

I have this code to mass rename all the .mkv & .mp4 files in the specific directory of my choosing:

$i = 0; Get-ChildItem "$env:Userprofile\Videos\Movie_Best_Clips" -Filter "vlc-record-2024*.mp4" -Recurse | %{Rename-Item $_.FullName -NewName ($_.Directory.Name+'_'+'_{0:D4}.mp4' -f $i++)};
$i = 0; Get-ChildItem "$env:Userprofile\Videos\Movie_Best_Clips" -Filter "vlc-record-2024*.mkv" -Recurse | %{Rename-Item $_.FullName -NewName ($_.Directory.Name+'_'+'_{0:D4}.mkv' -f $i++)};

There are multiple directories inside $env:Userprofile\Videos\Movie_Best_Clips in those folders are the files that match pattern vlc-record-2024*.mp4 or vlc-record-2024*.mkv.

Now I want to, without making any major code altering changes, include a check for whether a file with same name as the name made by iterator exists or not in the given directory, either before or inside Rename-Item line.

If such file already exists then just do continue like behaviour and increment the iterator and subsequently give a fresh name to file, to avoid rename-item errors.

So for example if file with name "MOVIENAME 2024 ENGLISH 1080P.mkv__0002.mp4" already exists(in the same directory as the existing "vlc-record-2024" file* that's to be renamed) then just rename the file in question with "MOVIENAME 2024 ENGLISH 1080P.mkv__0003.mp4" and like that on each and every iteration.

I saw this answer but the answerer has made the code too long, I want to do it in shortest way possible, but can't wrap my head around about how to achieve it, any help is appreciated.

Upvotes: 1

Views: 87

Answers (2)

Dom
Dom

Reputation: 418

Edit: got rid of flawed test-path statement and added wrapped the rename into try-catch for better troubleshooting

Get-ChildItem "C:\Users\kakoscdo-admin\Projekte\new2\logs" -Filter "*mkv" -Recurse  | sort CreationTime -Descending| ForEach-Object {
    $noNewNameFound = $true
    while($noNewNameFound) {
        $newName = $_.Directory.Name + '_' + '_{0:D4}.mkv' -f $i
        write-host "try renaming $($_.fullname) to $newName"
        if (test-path (Join-Path $_.DirectoryName $newname)) {
            $i++; 
            write-host "$newName already exists, try next"
        }
        else {
            write-host "new name not exist yet try renaming"
            write-host "$($_.parent)\$newname"
            try {
                Rename-Item $_.FullName -NewName $newName
                if($?) { write-host "successfully renamed" }
                $noNewNameFound = $false
            } catch {
                $noNewNameFound=$true; 
                write-host "error renaming $_.error, try next name"
            }
            $i++
        }
    }
}   

Upvotes: 1

Santiago Squarzon
Santiago Squarzon

Reputation: 60873

You could probably accomplish this in a single Get-ChildItem call using a hashtable that contains the index for each extension. There is also no need for a ForEach-Object loop if you use a delay-bind scriptblock.

$i = @{ '.mkv' = 0; '.mp4' = 0 }
$getChildItemSplat = @{
    Include = 'vlc-record-2024*.mkv', 'vlc-record-2024*.mp4'
    Recurse = $true
    Path    = "$env:Userprofile\Videos\Movie_Best_Clips"
}

Get-ChildItem @getChildItemSplat | Rename-Item -NewName {
    while ($true) {
        $name = '{0}_{1:D4}{2}' -f $_.Directory.Name, $i[$_.Extension]++, $_.Extension
        if (-not (Test-Path (Join-Path $_.DirectoryName $name))) {
            break
        }
    }

    $name
}

Upvotes: 2

Related Questions