10

我有几千个重复文件(例如 jar 文件),我想使用 powershell

  1. 递归搜索文件系统
  2. 查找副本(仅通过名称或校验和方法或两者)
  3. 删除除一个以外的所有重复项。

我是powershell的新手,我把它扔给了可能能提供帮助的PS人员。

4

4 回答 4

16

尝试这个:

ls *.txt -recurse | get-filehash | group -property hash | where { $_.count -gt 1 } | % { $_.group | select -skip 1 } | del

来自: http: //n3wjack.net/2015/04/06/find-and-delete-duplicate-files-with-just-powershell/

于 2016-03-07T18:46:05.937 回答
3

保留文件字典,当之前已经遇到下一个文件名时删除:

$dict = @{};
dir c:\admin -Recurse | foreach {
  $key = $_.Name #replace this with your checksum function
  $find = $dict[$key];
  if($find -ne $null) {
    #current file is a duplicate
    #Remove-Item -Path $_.FullName ?    
  }
  $dict[$key] = 0; #dummy placeholder to save memory
}

我使用文件名作为键,但如果需要(或两者),您可以使用校验和 - 请参阅代码注释。

于 2013-05-30T21:08:35.940 回答
3

尽管问题很老,但我一直需要根据内容清理所有重复文件。这个想法很简单,这个算法并不简单。这是接受“路径”参数以从中删除重复项的代码。

 Function Delete-Duplicates {
    param(
    [Parameter(
    Mandatory=$True,
    ValueFromPipeline=$True,
    ValueFromPipelineByPropertyName=$True
    )]
    [string[]]$PathDuplicates)

    $DuplicatePaths = 
        Get-ChildItem $PathDuplicates | 
        Get-FileHash |
        Group-Object -Property Hash |
        Where-Object -Property Count -gt 1 |
        ForEach-Object {
            $_.Group.Path |
            Select -First ($_.Count -1)}
    $TotalCount = (Get-ChildItem $PathDuplicates).Count
 Write-Warning ("You are going to delete {0} files out of {1} total. Please confirm the prompt" -f $DuplicatePaths.Count, $TotalCount)    
 $DuplicatePaths | Remove-Item -Confirm

    }

剧本

a) 列出所有 ChildItems

b) 从它们中检索 FileHash

c)按哈希属性对它们进行分组(因此所有相同的文件都在一个组中)

d) 过滤掉已经唯一的文件(组计数-eq 1)

e) 遍历每个组并列出除最后一个路径之外的所有路径 - 确保每个“哈希”的一个文件始终保留

f) 之前的警告,说明总共有多少个文件以及将要删除多少个文件。

可能不是最符合性能的选项(对每个文件进行 SHA1 处理),但可以确保文件是重复的。对我来说非常好:)

于 2014-12-19T12:47:41.123 回答
-1

您可以用快捷方式替换,而不仅仅是删除重复文件

#requires -version 3
<#
    .SYNOPSIS
    Script de nettoyage des doublons
    .DESCRIPTION
    Cherche les doublons par taille, compare leur CheckSum MD5 et les regroupes par Taille et MD5
    peut remplacer chacun des doubles par un lien vers le 1er fichier, l'original

    .PARAMETER Path
    Chemin ou rechercher les doublon

    .PARAMETER ReplaceByShortcut
    si specifier alors les doublons seront remplacé

    .PARAMETER MinLength
    ignore les fichiers inferieure a cette taille (en Octets)

    .EXAMPLE
    .\Clean-Duplicate '\\dfs.adds\donnees\commun'

    .EXAMPLE
    recherche les doublon de 10Ko et plus
    .\Clean-Duplicate '\\dfs.adds\donnees\commun' -MinLength 10000

    .EXAMPLE
    .\Clean-Duplicate '\\dpm1\d$\Coaxis\Logiciels' -ReplaceByShortcut
#>
[CmdletBinding()]
param (
    [string]$Path = '\\Contoso.adds\share$\path\data',
    [switch]$ReplaceByShortcut = $false,
    [int]$MinLength = 10*1024*1024 # 10 Mo
)

$version = '1.0'

function Create-ShortCut ($ShortcutPath, $shortCutName, $Target) {
    $link = "$ShortcutPath\$shortCutName.lnk"
    $WshShell = New-Object -ComObject WScript.Shell
    $Shortcut = $WshShell.CreateShortcut($link)
    $Shortcut.TargetPath = $Target
    #$Shortcut.Arguments ="shell32.dll,Control_RunDLL hotplug.dll"
    #$Shortcut.IconLocation = "hotplug.dll,0"
    $Shortcut.Description ="Copy Doublon"
    #$Shortcut.WorkingDirectory ="C:\Windows\System32"
    $Shortcut.Save()
    # write-host -fore Cyan $link -nonewline; write-host -fore Red ' >> ' -nonewline; write-host -fore Yellow $Target 
    return $link
}

function Replace-ByShortcut {
    Param(
        [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
            $SameItems
    )
    begin{
        $result = [pscustomobject][ordered]@{
            Replaced = @()
            Gain = 0
            Count = 0
        }
    }
    Process{
        $Original = $SameItems.group[0]
        foreach ($doublon in $SameItems.group) {
            if ($doublon -ne $Original) {
                $result.Replaced += [pscustomobject][ordered]@{
                    lnk = Create-Shortcut -ShortcutPath $doublon.DirectoryName -shortCutName $doublon.BaseName -Target $Original.FullName
                    target = $Original.FullName
                    size = $doublon.Length
                }
                $result.Gain += $doublon.Length
                $result.Count++
                Remove-item $doublon.FullName -force
            }
        }
    }
    End{
        $result
    }
}

function Get-MD5 {
    param (
        [Parameter(Mandatory)]
            [string]$Path
    )
    $HashAlgorithm = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
    $Stream = [System.IO.File]::OpenRead($Path)
    try {
        $HashByteArray = $HashAlgorithm.ComputeHash($Stream)
    } finally {
        $Stream.Dispose()
    }

    return [System.BitConverter]::ToString($HashByteArray).ToLowerInvariant() -replace '-',''
}

if (-not $Path) {
    if ((Get-Location).Provider.Name -ne 'FileSystem') {
        Write-Error 'Specify a file system path explicitly, or change the current location to a file system path.'
        return
    }
    $Path = (Get-Location).ProviderPath
}

$DuplicateFiles = Get-ChildItem -Path $Path -Recurse -File |
    Where-Object { $_.Length -gt $MinLength } |
        Group-Object -Property Length |
            Where-Object { $_.Count -gt 1 } |
                ForEach-Object {
                    $_.Group |
                        ForEach-Object {
                            $_ | Add-Member -MemberType NoteProperty -Name ContentHash -Value (Get-MD5 -Path $_.FullName)
                        }
                    $_.Group |
                        Group-Object -Property ContentHash |
                            Where-Object { $_.Count -gt 1 }
                }

$somme = ($DuplicateFiles.group | Measure-Object length -Sum).sum
write-host "$($DuplicateFiles.group.count) doublons, soit $($somme/1024/1024) Mo" -fore cyan

if ($ReplaceByShortcut) {
    $DuplicateFiles | Replace-ByShortcut
} else {
    $DuplicateFiles
}
于 2016-04-04T09:51:41.063 回答