PowerShellで重複ファイルを効率的に検出する方法

ネットワーク共有上の重複ファイルの処理は、特に自動化やディスクの乱雑化を抑えようとしている場合は、本当に頭の痛い作業です。ありがたいことに、PowerShellには便利なオプションがいくつか用意されていますが、ファイル数が多いとかなり遅くなるという難点があります。重要なのは、いつものように、処理を最適化することです。つまり、すべてのファイルをハッシュ化するのではなく、まずサイズで比較するのです。これはちょっとした回避策ですが、大きなフォルダでは処理速度が大幅に向上します。

PowerShell を使用してファイルサーバー上の重複ファイルを検索および管理する方法

方法 1: 単純なハッシュ比較 (遅いが簡単)

この方法は、すべてのファイルのハッシュを計算し、同じハッシュでグループ化することで重複ファイルを識別します。小さなフォルダや、精度が最優先される場合に便利です。ただし、設定によっては、特にファイルが数千個ある場合は、時間がかかる場合があります。ファイル数によっては数分、あるいはそれ以上かかる場合があります。この方法を実行する簡単な方法は次のとおりです。

Get-ChildItem –path C:\Share\ -Recurse | Get-FileHash | Group-Object -property hash | Where-Object { $_.count -gt 1 } | ForEach-Object { $_.group | Select-Object Path, Hash }

ハッシュが一致するファイルのパスを出力します。ちょっと変ですが、一部のマシンでは最初の実行時にブルースクリーンやハングアップが発生することがありますが、その後は突然正常に動作するようになります。キャッシュか、PowerShellの奇妙な挙動のせいかもしれません。

方法2: 最初にサイズフィルタリングを行って高速化する

ある設定では魔法のようにうまくいきましたが、別の設定では実行時間が10分から3秒にまで短縮されたと断言できます。冗談抜きで。基本的に、まずファイルをサイズで集めます。ファイルサイズ属性は読み取りが速く、ハッシュ化を必要としないためです。次に、同じサイズのファイル間でのみハッシュを比較します。手順は以下のとおりです。

$file_dublicates = Get-ChildItem –path C:\Share\ -Recurse | Group-Object -property Length | Where-Object { $_.count -gt 1 } | Select-Object –Expand Group | Get-FileHash | Group-Object -property hash | Where-Object { $_.count -gt 1 } | ForEach-Object { $_.group | Select-Object Path, Hash }

こうすることで、ハッシュ処理の量が全体的に減り、処理速度が向上します。Measure -Commandを使って両方のコマンドをテストし、比較することができます。

Measure-Command {  }

プロのヒント:数千ものファイルを扱う場合は、サイズ優先のルートを選ぶのがおすすめです。その方がずっと楽です。サイズフィルターを使うと、処理時間が数分から数秒に短縮されるケースがよくあります。

オプション: 重複を削除または移動するようにユーザーに促す

これはなかなか便利です。重複ファイルをまとめた後、ユーザーに削除または移動するファイルを選択させたい場合もあるでしょう。リストをOut-GridViewにパイプして選択可能なテーブルを表示し、選択したファイルを削除または移動します。

$file_dublicates | Out-GridView -Title "Select files to delete" -OutputMode Multiple –PassThru | Remove-Item –Verbose –WhatIf

グリッド内でCtrlキーを押しながら複数のファイルを選択し、「OK」をクリックすると、ファイルが削除されます(または別のフォルダに移動することもできます)。削除せずに整理したい場合は、Remove-ItemをMove-Itemに置き換えてください。

$file_dublicates | Out-GridView...| Move-Item -Destination D:\DuplicateBackups

あるいは、特に変更されないファイルの場合は、重複ファイルをハードリンクに置き換えて容量を節約することもできます。Winhanceというスクリプトがありますあるいは、重複ファイルを見つけて複数のコピーを保持する代わりにハードリンクを作成するスクリプトを独自に作成することもできます。リンクを直接作成する必要がある場合はfsutilを使用してください。ただし、管理者権限が必要なので注意してください。

param( [Parameter(Mandatory=$True)] [ValidateScript({Test-Path -Path $_ -PathType Container})] [string]$dir1, [Parameter(Mandatory=$True)] [ValidateScript({(Test-Path -Path $_ -PathType Container) -and $_ -ne $dir1})] [string]$dir2 ) Get-ChildItem -Recurse $dir1, $dir2 | Group-Object Length | Where-Object {$_. Count -ge 2} | Select-Object -ExpandGroup | Get-FileHash | Group-Object -Property hash | Where-Object { $_. Count -ge 2 } | ForEach-Object { $f1 = $_. Group[0].Path # Remove the duplicate file Remove-Item $f1 # Create a hard link (example, actual command may vary) # fsutil hardlink create $f1 $_. Group[1].Path }

このスクリプトは、変更されない静的ファイルには明らかに便利です。Windows Serverでは、データ重複除去などの機能によってほとんどの処理が裏で実行されますが、重複除去によってバックアップからの復元が複雑になる場合があるので注意が必要です。小規模な環境やスクリプト作成に熱心な方には、dupemergeなどのコンソールツールが、ハードリンクを持つファイルの置き換えを自動化するのに役立ちます。

全体的に見て、まずサイズでフィルタリングし、次にハッシュ化し、必要に応じてユーザープロンプトを使ってクリーンアップするという、適切なワークフローが考えられます。これは魔法ではなく、数千ものファイルを手動で検索するよりもはるかに優れた、実績のあるスクリプトです。