フォルダを整理していると、「中身は同じなのに名前が違うファイル」がいくつもできてしまうことがあります。ダウンロードの重複や、コピーの作りすぎが原因です。PowerShellを使えば、ファイル名ではなく中身(ハッシュ値)を基準に重複ファイルを検出でき、追加のソフトは要りません。
ポイントは、Get-FileHashで各ファイルのハッシュ値(中身から計算される指紋のような値)を求め、同じハッシュ値のファイルをGroup-Objectでグループ化することです。ハッシュ値が同じなら、名前が違っても中身は同一です。この記事では、実機のPowerShell 5.1で確認しながら、重複ファイルの検出と整理を整理します。
- 中身が同じかはハッシュ値で判定します。
Get-FileHashで求めます。 - 検出の基本は
Get-ChildItem -Recurse -File | Get-FileHash | Group-Object Hash | Where-Object { $_.Count -gt 1 }です。 - ファイル名が違っても、中身が同じなら検出できます。
- 大量のファイルは、先に
Length(サイズ)で絞ると高速になります。 - 削除するときは各グループで1つ残し、必ず
-WhatIfで確認してから実行します。 - 既定のアルゴリズムは
SHA256。速さ重視ならMD5も選べます。
絞り込みのWhere-Object・Group-ObjectはWhere-Object・Select-Objectでデータを絞り込む、対象の取得はファイル・フォルダ操作、結果の処理はループ完全ガイドもあわせて参考になります。
考え方:ハッシュで「中身が同じ」を判定する
重複検出のカギはハッシュ値です。ハッシュ値とは、ファイルの中身から計算される固定長の文字列で、中身が1ビットでも違えば全く別の値になり、中身が同じなら必ず同じ値になります。つまり、ファイル名や更新日時に関係なく、ハッシュ値を比べれば中身が同じかどうかを判定できます。
ファイルのハッシュを取得する Get-FileHash
1つのファイルのハッシュ値はGet-FileHashで求められます。結果のHashプロパティが、そのファイルの中身を表す値です。
# ファイルのハッシュ値を求める(既定は SHA256) Get-FileHash "C:\data\report.txt" # ハッシュ値だけ取り出す (Get-FileHash "C:\data\report.txt").Hash # 出力例: # Algorithm Hash Path # --------- ---- ---- # SHA256 A1B2C3...(64文字の16進数) C:\data\report.txt
実機でも、Get-FileHashの既定アルゴリズムはSHA256で、.AlgorithmにSHA256と表示されました。2つのファイルのHashが一致すれば中身は同一、違えば中身は異なる、と判断できます。あとはこれを全ファイルに適用し、同じハッシュ値のものをまとめれば、重複が見つかります。
重複を検出する基本パイプライン
フォルダ内の重複ファイルは、次のパイプライン1行で検出できます。各コマンドが「対象を集める→ハッシュを求める→ハッシュでグループ化→2件以上のグループだけ残す」という流れになっています。
# フォルダ内(サブフォルダ含む)の重複ファイルを検出
Get-ChildItem "C:\data" -Recurse -File |
Get-FileHash |
Group-Object Hash |
Where-Object { $_.Count -gt 1 }
# 各ステップの意味:
# Get-ChildItem -Recurse -File … サブフォルダも含めてファイルを集める
# Get-FileHash … それぞれのハッシュ値を求める
# Group-Object Hash … 同じハッシュ値ごとにまとめる
# Where-Object Count -gt 1 … 2件以上(=重複)のグループだけ残す
実機で、a.txt・copy_of_a.txt・サブフォルダのdup.txtという名前もフォルダも違う3つの同一内容ファイルを用意して検出したところ、ハッシュ値が同じ1つのグループ(Countが3)として正しくまとまりました。Group-Object Hashはハッシュ値でグループ化するので、ファイル名はいっさい関係ありません。Where-Object { $_.Count -gt 1 }で「2件以上=重複」だけに絞り込んでいます。重複が無ければ結果は何も表示されません。
重複したファイルの一覧を見やすく表示する
検出結果をそのまま見ても分かりにくいので、各グループのファイルのパスを一覧で表示すると確認しやすくなります。Groupプロパティに、そのグループに含まれるファイルが入っています。
$dups = Get-ChildItem "C:\data" -Recurse -File |
Get-FileHash |
Group-Object Hash |
Where-Object { $_.Count -gt 1 }
# 各グループの重複ファイルを一覧表示
foreach ($group in $dups) {
Write-Host "■ 重複($($group.Count) 件):"
$group.Group | ForEach-Object { Write-Host " $($_.Path)" }
}
$dupsに検出結果を入れておき、foreachで各グループを回して、$group.Groupに含まれるファイルのパスを表示しています。これで「どのファイルとどのファイルが同じ中身か」が一目で分かります。削除はこの一覧を確認してから行うのが安全です。
サイズで事前に絞り込んで高速化する
ファイル数が多いと、すべてのファイルのハッシュ計算に時間がかかります。中身が同じファイルは必ずサイズも同じなので、先にLength(サイズ)でグループ化し、同じサイズが2件以上あるものだけハッシュを計算すると、大幅に速くなります。
# まず同じサイズのファイルだけに絞ってから、ハッシュで確定する
Get-ChildItem "C:\data" -Recurse -File |
Group-Object Length |
Where-Object { $_.Count -gt 1 } | # 同サイズが複数あるものだけ
ForEach-Object { $_.Group } | # そのファイル群を取り出す
Get-FileHash |
Group-Object Hash |
Where-Object { $_.Count -gt 1 }
実機でも、まずGroup-Object Lengthで同じサイズのファイルをまとめ、複数あるものだけをハッシュ計算の対象にできました。サイズが違えば中身も必ず違うため、サイズが1つしかないファイルはハッシュ計算をスキップできます。ハッシュ計算はファイルを丸ごと読むため重い処理です。何千・何万ファイルもあるフォルダでは、この事前フィルタの有無で処理時間が大きく変わります。少数のファイルなら、最初の基本パイプラインのままで十分です。
重複を削除する(1つ残して消す)
重複を整理するときは、各グループで1つだけ残し、残りを削除します。Select-Object -Skip 1で「先頭の1つを除いた残り」を削除対象にできます。削除は取り返しがつかないため、必ず先に-WhatIfで確認してください。
# 各グループで先頭1つを残し、残りを削除対象にする
$toDelete = Get-ChildItem "C:\data" -Recurse -File |
Get-FileHash |
Group-Object Hash |
Where-Object { $_.Count -gt 1 } |
ForEach-Object { $_.Group | Select-Object -Skip 1 }
# まず -WhatIf で「何が消えるか」だけ確認(実際には消さない)
$toDelete | ForEach-Object { Remove-Item $_.Path -WhatIf }
# 内容を確認して問題なければ、-WhatIf を外して実行
# $toDelete | ForEach-Object { Remove-Item $_.Path }
実機で確認したところ、Select-Object -Skip 1を使うと、3つの重複ファイル(1.txt・2.txt・3.txt)のうち先頭の1.txtを残し、2.txt・3.txtの2つだけが削除対象になりました。-WhatIfを付けて実行すると「対象 … に対して操作 “ファイルの削除” を実行しています」と消す予定のファイルが表示されるだけで、実際には削除されません。表示された内容に問題がないことを必ず確認し、それから-WhatIfを外して本実行してください。大事なファイルを誤って消さないために、いきなり本実行しないことが重要です。削除前にバックアップを取っておくと、さらに安全です。
アルゴリズムの選択 SHA256とMD5
Get-FileHashの既定はSHA256ですが、-Algorithmで変更できます。重複検出の用途なら、より高速なMD5を使う選択肢もあります。
# 既定(SHA256・安全性が高い・64文字) Get-FileHash "C:\data\a.txt" # MD5(高速・32文字)— 重複検出の用途なら十分 Get-FileHash "C:\data\a.txt" -Algorithm MD5 # 指定できる主な値: SHA256, SHA1, MD5 など
実機でも、-Algorithm MD5を指定すると32文字のハッシュ値(SHA256は64文字)が得られました。MD5はセキュリティ用途では非推奨ですが、「中身が同じかを調べる」だけの重複検出なら、高速なMD5でも実用上問題ありません。ファイル数が非常に多く速度を優先したいときは、MD5とサイズ事前フィルタを組み合わせると効率的です。確実性を重視するなら既定のSHA256のままで構いません。
主なコマンドまとめ
重複ファイル検出で使う要素をまとめます。
| 要素 | 働き |
|---|---|
Get-ChildItem -Recurse -File |
サブフォルダ含めファイルを集める |
Get-FileHash |
中身からハッシュ値を求める(既定SHA256) |
Group-Object Hash |
同じハッシュ値ごとにまとめる |
Where-Object { $_.Count -gt 1 } |
2件以上(=重複)だけ残す |
Group-Object Length |
サイズで事前に絞る(高速化) |
Select-Object -Skip 1 |
1つ残して削除対象を作る |
Remove-Item -WhatIf |
削除内容を確認(実際には消さない) |
よくある失敗
ファイル名で重複を判定しようとする
名前が違う同一ファイルは見つかりません。中身(ハッシュ値)で判定します。
大量ファイルをいきなり全部ハッシュ計算する
遅くなります。先にGroup-Object Lengthでサイズが同じものに絞ると高速です。
確認せずにいきなり削除する
必ず-WhatIfで消える対象を確認してから本実行します。バックアップも推奨です。
全グループを消してしまう
Select-Object -Skip 1で各グループ1つは残します。これを忘れると全部消えます。
-File を付けずフォルダまで対象にする
Get-ChildItemに-Fileを付け、ファイルだけを対象にします。
よくある質問
Get-FileHashはファイルの中身からハッシュ値を計算し、Group-Object Hashはそのハッシュ値でグループ化するため、ファイル名や置き場所に関係なく、中身が同じものを重複として検出できます。Group-Object Lengthでサイズが同じファイルだけに絞り、複数あるものだけをハッシュ計算してください。中身が同じならサイズも必ず同じなので、サイズが単独のファイルはハッシュ計算を省けます。ハッシュは重い処理なので、この事前フィルタで大きく高速化できます。Select-Object -Skip 1を使い、1つ残して残りを削除対象にします。そして必ずRemove-Item -WhatIfで「何が消えるか」を確認してから、-WhatIfを外して本実行してください。削除は取り消せないため、バックアップを取っておくとより安全です。SHA256、速度を優先したいならMD5が選べます。MD5はセキュリティ用途では非推奨ですが、ファイルが同じかを調べるだけなら実用上問題ありません。Where-Object { $_.Count -gt 1 }で残るグループが無く、何も表示されないのが正常です。コマンド自体が失敗しているわけではありません。試しに同じファイルをコピーして実行すると、検出される様子を確認できます。まとめ
- 重複は中身(ハッシュ値)で判定します。名前は関係ありません。
- 基本は
Get-ChildItem -Recurse -File | Get-FileHash | Group-Object Hash | Where-Object { $_.Count -gt 1 }。 - 大量ファイルは先に
Group-Object Lengthでサイズ絞り込みで高速化します。 - 削除は各グループで
Select-Object -Skip 1、必ず-WhatIfで確認してから。 - 既定は
SHA256、速度重視ならMD5も使えます。
PowerShellなら、追加ソフトなしで重複ファイルを中身ベースで正確に検出できます。「ハッシュでグループ化する」という考え方さえつかめば、応用は簡単です。削除のときだけは-WhatIfでの確認を忘れず、安全にフォルダを整理しましょう。

