最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

windows - How to batch run a script that finds duplicates, replacing them with the smaller file, across directories? - Stack Ove

programmeradmin2浏览0评论

I have a script i've mixed and matched and can't get working (at the bottom). To give an example: I'm using powershell to find duplicate files - file names. I have a file - WhatzIt.mp3 - which is the one I want to keep, because it's the smallest in that particular encoding. I have older/larger versions of the file in other directories - with different extensions - that i would want to overwrite with that file.

Example:

  • FolderA - WhatzIt.mp3 5mb

  • FolderB - WhatzIt.m4a 10mb

  • FolderB - WhatzIt.opus 7mb

  • FolderB - WhatzIt.mp3 8mb

Desired Result:

  • FolderA - WhatzIt.mp3 5mb

  • FolderB - WhatzIt.mp3 5mb

I want a recursive search that in selected (2 or more, FolderA + FolderB) directories and their subdirectories, would recognise there are (in this example) 4 files that have the same filename ('WhatzIt', possibly case sensitive, ignoring extension differences) Which then allows me in way, to automatically recognise and copy the smaller file (WhatzIt.mp3) into the directories that were found to have files with the same name (WhatzIt.,4a, WhatzIt.opus), potentially overwriting files (WhatzIt.mp3 in FolderB). And deleting the other larger files with the same name (WhatzIt.m4a, WhatzIt.opus).

Here is the code i've already tried - while i googled, i know almost nothing about how or why.


$aDir = "C:\FolderA"
$bDir = "C:\FolderB"

$aFiles = Get-ChildItem -Path "$aDir" -Recurse
$bFiles = Get-ChildItem -Path "$bDir" -Recurse

ForEach ($file in $aFiles) {
    if(Test-Path $bFiles) | Where-Object {$_.BaseName -eq $aFiles} {
        Write-Output "$file exists in $bDir. Copying."
        Remove-Item $bDir\$file -recurse | Where-Object {$_.BaseName -eq $aFile}
        Copy-Item $aDir\$file $bDir -recurse -include *.opus, *.mp3,*.m4a
        
    } else {
        Write-Output "$file does not exist in $bDir."
    }
}

The script output (or did) the write-output portion, but doesn't seem to process or consider the rest of the code. I think it manages to make the comparison between directories - MAYBE NOT - but then does nothing else.

I have a script i've mixed and matched and can't get working (at the bottom). To give an example: I'm using powershell to find duplicate files - file names. I have a file - WhatzIt.mp3 - which is the one I want to keep, because it's the smallest in that particular encoding. I have older/larger versions of the file in other directories - with different extensions - that i would want to overwrite with that file.

Example:

  • FolderA - WhatzIt.mp3 5mb

  • FolderB - WhatzIt.m4a 10mb

  • FolderB - WhatzIt.opus 7mb

  • FolderB - WhatzIt.mp3 8mb

Desired Result:

  • FolderA - WhatzIt.mp3 5mb

  • FolderB - WhatzIt.mp3 5mb

I want a recursive search that in selected (2 or more, FolderA + FolderB) directories and their subdirectories, would recognise there are (in this example) 4 files that have the same filename ('WhatzIt', possibly case sensitive, ignoring extension differences) Which then allows me in way, to automatically recognise and copy the smaller file (WhatzIt.mp3) into the directories that were found to have files with the same name (WhatzIt.,4a, WhatzIt.opus), potentially overwriting files (WhatzIt.mp3 in FolderB). And deleting the other larger files with the same name (WhatzIt.m4a, WhatzIt.opus).

Here is the code i've already tried - while i googled, i know almost nothing about how or why.


$aDir = "C:\FolderA"
$bDir = "C:\FolderB"

$aFiles = Get-ChildItem -Path "$aDir" -Recurse
$bFiles = Get-ChildItem -Path "$bDir" -Recurse

ForEach ($file in $aFiles) {
    if(Test-Path $bFiles) | Where-Object {$_.BaseName -eq $aFiles} {
        Write-Output "$file exists in $bDir. Copying."
        Remove-Item $bDir\$file -recurse | Where-Object {$_.BaseName -eq $aFile}
        Copy-Item $aDir\$file $bDir -recurse -include *.opus, *.mp3,*.m4a
        
    } else {
        Write-Output "$file does not exist in $bDir."
    }
}

The script output (or did) the write-output portion, but doesn't seem to process or consider the rest of the code. I think it manages to make the comparison between directories - MAYBE NOT - but then does nothing else.

Share Improve this question asked Jan 19 at 1:53 KatCatKatCat 12 bronze badges 1
  • So, if I understand the issue correctly, you only get "$file does not exist in $bDir." as a result for every $file? – iRon Commented Jan 19 at 8:49
Add a comment  | 

2 Answers 2

Reset to default 0

It seems you are telling us that you would always prefer to have the file in FolderA copied to FolderB as it is smaller and newer than the files in FolderB, right?
At the same time remove all files with a similar BaseName in FolderB but having a different extension
(any of '.opus', '.mp3','.m4a')

To do that, I would suggest using Get-ChildItem to get only the files with any of the three extensions mentioned first and then loop over the list from your FolderA to find and remove files in FolderB.

# append switch -File so your lists will not also contain directories
$aFiles = Get-ChildItem -Path $aDir -File -Include '*.opus', '*.mp3','*.m4a' -Recurse
$bFiles = Get-ChildItem -Path $bDir -File -Include '*.opus', '*.mp3','*.m4a' -Recurse

# loop over the source files in FolderA
foreach ($file in $aFiles) {
    # delete the B files with the same BaseName as the current file from the A folder
    $bFiles | Where-Object {$_.BaseName -eq $file.BaseName} | ForEach-Object {
        Write-Host "Deleting '$($_.FullName)'..."
        $_ | Remove-Item -WhatIf   # see below about the WhatIf switch
    }
    # next, copy the file from the A folder to the B folder
    $file | Copy-Item -Destination $bDir
}

I have added -WhatIf to the destructive Remove-Item command so what will now happen is that you will only see in the console which files would be deleted. In reality, nothing actually gets removed. If the output in the console is what you expect it to be, remove the -WhatIf switch and run the code again

$DirectoryArray = '.\Folder 1', '.\Folder 2', '.\Folder 3'

$ExtensionArray = '.mp3', '.opus', '.m4a'

# Get all Files from the selected directories.   
$DuplicateArray = Get-ChildItem -Path $DirectoryArray -Recurse -Depth 1 -File | 
    # Cannot use -Filter for multiple elements in Get-ChildItem so let's filter here.  
    Where-Object Extension -In $ExtensionArray |
    # Group the files by BaseName, so without the extension.  
    Group-Object -Property BaseName |
    # Skip files with only 1 entry.   
    Where-Object -Property Count -GT 1

# For each group of duplicate names.   
foreach ($DuplicateSet in $DuplicateArray) {
    # Order the files by size, biggest to smallest.   
    $AllFiles = $DuplicateSet.Group | Sort-Object -Property Length -Descending 

    # Send all files except the last one(the smallest) to be deleted.   
    $AllFiles | Select-Object -SkipLast 1 | Remove-Item -WhatIf

    # For each Directory.   
    # Select the last = smallest file and copy it to the directory.  
    foreach ($Destination in $DirectoryArray) { 
        # -ErrorAction SilentlyContinue because it will try to copy it over itself, and that's a error we can ignore.   
        # But using SilentlyContinue let's us keep logging errors should something unforeseen happen.   
        $AllFiles | Select-Object -Last 1 | Copy-Item -Destination $Destination -ErrorAction SilentlyContinue -WhatIf 
    }
}



#>

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论