Cómo detectar archivos duplicados de forma eficiente con PowerShell

Gestionar archivos duplicados en un recurso compartido de red puede ser un verdadero dolor de cabeza, especialmente cuando se intenta automatizar el proceso o controlar el desorden del disco. Afortunadamente, PowerShell ofrece algunas opciones decentes, pero el problema es que puede volverse bastante lento con una gran cantidad de archivos. La clave, como siempre, es optimizar el proceso: comparar los archivos primero por tamaño en lugar de aplicar un hash a todo de inmediato. Es una solución alternativa, pero agiliza considerablemente el proceso en carpetas grandes.

Cómo encontrar y administrar archivos duplicados en un servidor de archivos con PowerShell

Método 1: Comparación de hash simple (lenta, pero sencilla)

Este método calcula los hashes de todos los archivos y los agrupa por el mismo hash para identificar duplicados. Es útil para carpetas pequeñas o cuando la precisión es fundamental. Normalmente, en algunas configuraciones, puede tardar un poco, especialmente si tiene miles de archivos. Su ejecución puede durar unos minutos o incluso más, dependiendo del número de archivos. Una forma sencilla de ejecutarlo es:

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

Mostrará rutas de archivos coincidentes con hashes idénticos. Es un poco raro, pero en algunas máquinas, la primera ejecución puede mostrar una pantalla azul o un bloqueo, y luego, de repente, funciona correctamente. Posiblemente se trate del almacenamiento en caché o de alguna peculiaridad de PowerShell.

Método 2: Acelere con el filtrado de tamaño primero

En una configuración funcionó de maravilla, pero en otra, juro que redujo el tiempo de ejecución de 10 minutos a 3 segundos, ¡en serio! Básicamente, primero se recopilan los archivos por tamaño, ya que los atributos de tamaño de archivo se leen rápidamente y no requieren hashes. Luego, solo se comparan los hashes entre archivos del mismo tamaño. Así es como se ve:

$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 }

De esta manera, se reduce el hash en general, lo que lo hace más rápido. Puedes probar y comparar ambos comandos con Measure-Command, así:

Measure-Command {  }

Consejo profesional: si trabajas con miles de archivos, prioriza el tamaño; es mucho menos tedioso. He visto con frecuencia que el filtro de tamaño reduce el tiempo de procesamiento de varios minutos a solo unos segundos.

Opcionalmente: solicitar al usuario que elimine o mueva duplicados

Esto es bastante interesante: después de recopilar los duplicados, podrías permitir que el usuario elija qué eliminar o mover. Canaliza la lista a Out-GridView para mostrar una tabla seleccionable y, a continuación, elimina o mueve los archivos seleccionados:

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

Ahora, en la cuadrícula, mantenga presionada la tecla CTRL para seleccionar varios archivos, haga clic en Aceptar y se eliminarán (o se moverán a otra carpeta si lo prefiere).Si desea mantener todo organizado sin eliminar, reemplace Remove-Item por Move-Item :

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

O incluso reemplazar duplicados con enlaces físicos para ahorrar espacio, especialmente si esos archivos no van a cambiar. Existe un script llamado Winhance, o puedes crear uno propio que encuentre duplicados y cree enlaces físicos en lugar de guardar varias copias. Usa fsutil si necesitas crear enlaces directamente, pero ten cuidado, ya que requiere permisos de administrador.

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 }

Este script es útil, obviamente, para archivos estáticos que no cambian. En Windows Server, funciones como la deduplicación de datos pueden realizar la mayor parte de esto de forma automática, pero tenga cuidado: a veces la deduplicación complica las restauraciones a partir de copias de seguridad. Para configuraciones más pequeñas o para quienes se dedican a la creación de scripts, una herramienta de consola como dupemerge puede automatizar eficazmente la sustitución de archivos con enlaces físicos.

En general, un flujo de trabajo decente implica filtrar primero por tamaño, luego aplicar hash y, finalmente, usar indicaciones al usuario para la limpieza si es necesario. No hay magia, solo scripts de eficacia comprobada que superan con creces la búsqueda manual entre miles de archivos.