PowerShell : Compare-Object est votre amie (exemple sur synchronisation répertoire par répertoire)
Aujourd'hui je me suis retrouvé avec un besoin relativement basique : effectuer un mirroir d'un répertoire donné.
Jusque là, pas de problème, l'ami robocopy et son paramètre MIR sont plutôt sympas pour ce genre de choses :
robocopy.exe ".\Rep" "\\Dest\Mirroirs\Rep" /MIR /FFT
Seulement voilà, executer cette commande avec une destination ne contenant aucun des fichiers de la source équivaut bien évidemment à la copie de toutes les données, dans mon cas ça veut dire 9 sous-répertoires totalisant 60Go de données.
Vu le volume et la vitesse du réseau (un petit 100Mbps), je préfère faire une copie sous-répertoire par sous-répertoire vers la destination avant de mettre en place la commande de mirroir qui se chargera de garder tout celà à jour.
Rien de bien compliqué :
robocopy.exe ".\Rep\SousRep1" "\\Dest\Mirroirs\Rep\SousRep1" /ZB /E
Sauf que je suis du genre flemmard et que je n'ai pas envie de faire 9 fois la modification de la commande à la main.
L'idéal serait donc de pouvoir lancer 9 fois (je veux garder le choix du moment de démarrage de chaque copie) un script qui choissise automatiquement le premier sous-répertoire de la source qui n'existe pas encore dans la destination.
Facile avec PowerShell : vous disposez de base de la sympathique Cmdlet Compare-Object qui comme l'indique permet de faire des comparaisons entre les paramètres ReferenceObject et DifferenceObject.
C'est exactement ce qu'il nous faut, une liste de répertoires pouvant bien entendu servir d'entrée pour cette cmdlet :-)
Prenez ce code, Src\Rep contenant 9 sous-répertoires et Dest\Rep étant vide :
$srcDir="D:\Temp\Tests\Src\Rep"
$targetDir="D:\Temp\Tests\Dest\Rep"
Compare-Object $(dir $srcDir | where{$_.PSIsContainer}) $(dir $targetDir | where{$_.PSIsContainer})
Vous obtenez :
InputObject SideIndicator
----------- -------------
SousRep1 <=
SousRep2 <=
SousRep3 <=
SousRep4 <=
SousRep5 <=
SousRep6 <=
SousRep7 <=
SousRep8 <=
SousRep9 <=
SideIndicator indique de quel côté se trouve le répertoire qui manque de l'autre côté : ici nous voyons donc que les sous-répertoires n'existent que dans la source.
La même commande executée avec le sous-répertoire "SousRep1" dans le répertoire cible donnera la sortie suivante :
InputObject SideIndicator
----------- -------------
SousRep2 <=
SousRep3 <=
SousRep4 <=
SousRep5 <=
SousRep6 <=
SousRep7 <=
SousRep8 <=
SousRep9 <=
Si vous désirez voir les répertoires existants des 2 côtés, le paramètre IncludeEqual est fait pour vous.
Mais là ça ne nous intéresse bien évidemment pas, nous allons de toute façon filtrer sur SideIndicator = "<=".
Dans le cas qui nous intéresse l'utilisation sera la suivante :
# répertoire source
$srcDir="D:\Temp\Tests\Src\Rep"
# répertoire cible
$targetDir="D:\Temp\Tests\Dest\Rep"
# définition du template de commande pour Robocopy
$robocopyCommand='robocopy.exe "$srcDir\$currentFolder" "$targetDir\$currentFolder" /ZB /E'
# Execution :
# 1) Comparaison des listes des répertoires
# 2) Filtrage pour obtenir seulement la liste de ceux absent de la destination
# 3) Sélection du premier qui se trouve dans ce cas
# 4) Execution de la commande Robocopy pour effectuer la copie
Compare-Object $(dir $srcDir | where{$_.PSIsContainer}) $(dir $targetDir | where{$_.PSIsContainer}) | where {$_.SideIndicator -eq "<="} | select -First 1 | % { $currentFolder=$_.InputObject; Invoke-Expression -Command $robocopyCommand }
Ce script lancera ainsi une commande différente à chaque execution :
- robocopy.exe "D:\Temp\Tests\Src\Rep\SousRep1" "D:\Temp\Tests\Dest\Rep\SousRep1" /ZB /E
- robocopy.exe "D:\Temp\Tests\Src\Rep\SousRep2" "D:\Temp\Tests\Dest\Rep\SousRep2" /ZB /E
- etc
Ce qui me permet de faire ma mise en place en 9 fois sans me fatiguer plus que ça.
L'exemple pris ici n'est pas forcément parlant (et utile) pour tout le monde mais je pense qu'il s'agit cependant d'un exemple assez intéressant de l'utilisation de Compare-Object.
Bon scripting.
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :