Zum Finden und Löschen von doppelten Dateien gibt es jede Menge Tools, aber es geht auch von der Kommandozeile mit einem Powershell-Einzeiler. Dafür ist ein Cmdlet nützlich, dass ich normalerweise nur selten einsetze, dass hier aber seine Mächtigkeit ausspielt: Group-Object.
Mit Group-Object kann man Objekte in ein Array zusammenfassen (also gruppieren), die in einer Eigenschaft gleich sind, also z.B. nach dem Namen, dem Erstelldatum usw. Die ausgegebenen Gruppen beinhalten dann nicht nur die gruppierten Objekte, sondern auch die Menge der Objekte in einer Gruppe sowie die Eigenschaft, die gruppiert wurde. Wenn man das auf die Dateien des Windows\System32-Ordners anwendet, sieht das ungefähr so aus:
Die Eigenschaft Count gibt an, wie viele Elemente eine Gruppe enthält. Sortiert man nach der Anzahl und filtert alle Gruppen aus, die weniger als 2 Elemente beinhalten, erhält man alle Dateien gleicher Größe in einer Gruppe. Als Beispiel verwende ich hier den Ordner Windows\System32, damit man anhand realer Dateien die Ausgabe nachvollziehen kann.
Anschließend muss man die Gruppen mit einem Foreach-Object wieder auflösen und mit dem Parameter -Skip des Select-Object das jeweils erste Element - das beibehalten werden soll - entfernen. Die Ausgabe kann man dann innerhalb des Skriptblocks des Foreach-Object in den Remove-Item pipen, was im Code ausgelassen wurde, um bösen Unfällen vorzubeugen.
Die Gruppierung der Dateien nach Größe geht sehr schnell, ist aber kein 100%iger Indikator für die Eindeutigkeit von Dateien, speziell, wenn es sich um sehr kleine Dateien handelt. Diese Variante funktioniert sehr gut, wenn es sich um sehr große Dateien handelt, bei der der Verlust einer einzelnen Datei keine katastrophalen Auswirkungen hat, da sie wahnsinnig schnell ist.
Um die Eindeutigkeit der Dateien sicherzustellen, muss man zuerst eine Prüfsumme in Form eines Hashes über die Dateien erstellen, was mit dem Cmdlet Get-Filehash sehr einfach zu bewerkstelligen ist. Dafür piped man die Ausgabe von Get-Childitem einfach zuerst in Get-Filehash und gruppiert dann nach dem erstellten Hash. Der modifizierte Code sieht dann so aus:
Im Beispielcode wurde SHA1 als als Hash-Algorithmus angegeben. Lässt man den Parameter aus, verwendet Get-Filehash den SHA256-Algorithmus, der längere Hashes erzeugt, aber auch ein wenig langsamer ist. Die Eindeutigkeit ist bei beiden Algorithmen sicher gestellt.
Ein paar Hinweise zur Performance: Es hat in meinen Tests keinen merklichen Unterschied gemacht, ob ich Powershell 5.1 oder Powershell 7 eingesetzt habe. Die Hash-Algorithmen sind aber so schnell, dass es einen Unterschied macht, ob die Dateien im Netzwerk oder lokal liegen! Im Zweifel sollte man die Duplikatssuche immer lokal ausführen oder eine schnelle Netzwerkanbindung bevorzugen. Zum Erstellen der Hashes müssen die Dateien aber immer erst lokal über das Netzwerk gezogen werden, man generiert bei großen Datenmengen also jede Menge Netzwerklast! Wenn es schnell gehen soll, ist es also eine gute Idee, die Suche die Dateigröße einzuschränken und nur auf die Dateien gleicher Größe eine Hash-Prüfung zu machen, was den Suchprozess massiv beschleunigt. Das fertige Skript sieht dann also so aus - wieder ohne Remove-Item:
Ich habe das ganze noch einmal in eine Funktion gegossen, die einen Ordner auch rekursive nach Duplikaten untersuchen kann, aber keinen Löschvorgang ausführt, sondern nur die gruppierten Duplikate ausgibt.