Dans cet article, nous verrons comment faire pour comparer l'intégrité des données entre deux fichiers, de manière totalement automatisée et rapide à l'aide de d'un Checksum MD5 et de Powershell.
Comme il n'est pas impossible qu'une copie de données d'un endroit A à un endroit B se termine correctement, ou parce qu'un fichier pourrait avoir été judicieusement modifié pour des raisons volontaires de nuire (par exemple), ou parce qu'il est nécessaire de comparer que les données sur le support de stockage A sont bien toujours les mêmes que sur le support B (ce qui est mon cas), il est parfois nécessaire de s'assurer que les données sont en tout point similaires.
Heureusement pour nous, Powershell fournit les outils nécessaires pour TOUT faire sans avoir besoin d'ajouter quoi que ce soit. Nous allons donc dans l'ordre :
Pour le point 1, c'est assez facile. Nous utilisons la commande Get-Childitem pour récupérer l'intégralité des fichiers en A et en B. En gardant uniquement les propriétés qui nous intéressent, ici "Fullname" (le chemin complet) et "Name" (le nom du fichier)
Pour le point 2, nous nous servirons de .NET afin de pouvoir désigner le fichier et son mode d'ouverture (ici, Open et Read, puisque nous l'ouvrons pour ensuite le lire). Nous utiliserons également .NET pour convertir les bits en une String lisible pour créer un MD5.
Pour le point 3, nous créerons un objet de type System.Security.Cryptography.MD5CryptoServiceProvider (rien que ça) pour pouvoir récuperer le Checksum MD5 et enfin...
Pour le point 4 nous utiliserons la commande "Compare-Object" pour vérifier que les checksum de chaque fichier en A et en B correspondent.
Pour cet exemple, j'ai crée dans un dossier A 100 fichiers contenant chacun un nombre aléatoire et dans un dossier B 100 fichiers contenants d'auters nombres aléatoires. J'ai ensuite recopié certains fichiers de A vers B pour avoir des similitudes, et avoir également des fichiers différents.
#On obtient la liste des fichiers dans le dossier A
$file1 = Get-ChildItem U:\Checksum\liste_fichiers_1 | Select-Object -Property fullname,name
#On fait de même avec la liste de fichiers dans B
$file2 = Get-ChildItem U:\Checksum\liste_fichiers_2 | Select-Object -Property fullname,name
#On crée un objet d'encryptage MD5
$md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
#On crée un tableau qui sera utilisé pour l'enregistrement des résultats
$array = New-Object System.Collections.ArrayList
#Pour chaque fichier dans la variable $file1
foreach ($file in $file1)
{
#On trouve son équivalent dans l
$file_file2 = $file2 | Where-Object {$_.name -eq $file.name}
#############################
# POUR LE FICHIER 1 #
#############################
#On ouvre en lecture le fichier
$stream = [System.IO.File]::Open($file.fullname,[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
#on crée un hash à partir du MD5. Nous créeons un flux de Bits que nous convertissons en String
$hash = [System.BitConverter]::ToString($md5.ComputeHash($stream))
#Nous libérons l'utilisation du fichier
$stream.Close()
#Nous ajoutons les informations du fichier dans le tableau
$array.Add([pscustomobject]@{
"fichier" = $file.fullname
"Hash" = $hash
}) | out-null
#############################
# POUR LE FICHIER 2 #
#############################
#On fait pareil que le fichier 1!
$stream = [System.IO.File]::Open($file_file2.fullname,[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
$hash = [System.BitConverter]::ToString($md5.ComputeHash($stream))
$stream.Close()
$array.Add([pscustomobject]@{
"fichier" = $file_file2.fullname
"Hash" = $hash
}) | out-null
}
#Nous créons ensuite un tableau qui contiendra les résultats des comparaisons
$resultsarray = New-Object System.Collections.ArrayList
#Nous faisons une boucle for qui commence de 0 et qui s'arrête au dernier index du tableau. Nous avançons de 2 en 2 car nous comparons 2 fichiers à chaque fois, l'original et le nouveau.
for ($start = 0;$start -lt $array.Count;$start +=2)
{
#Nous comparons le hash de l'objet situé en ligne X du tableau avec celui situé en X+1
$compareresult = Compare-Object $array[$start].hash $array[$start+1].hash
#Si le resultat differe
if ($compareresult)
{
#On ajoute dans le tableau les informations de chaque hash par rapport au nom de fichier
$resultsarray.add([pscustomobject]@{
"fichier" = $array[$start].fichier
"checksum1" = $compareresult[0].InputObject
"checksum2" = $compareresult[1].InputObject}) | Out-Null
Write-Host "Checksum different sur $($array[$start].fichier)"
}
}
$resultsarray
Et voila le résultat!
Ce script est volontairement simpliste. Il lui manque quelques informations pour être complétement utilisable, par exemple vérifier que la lecture de chaque fichier est ok, gérer les exceptions en créant des entrées dans le tableau pour chaque erreur afin de conserver une cohérence lors de l'avancée dans la boucle for()...
Mais l'objectif de cet article n'est pas de fournir un script tout fait. Je l'ai écrit pour vous montrer principalement comment on peut facilement vérifier que l'intégrité des données est conservée pour de nombreux fichiers en un temps moindre grâce à Powershell.
En espérant que cela vous serve :)