Cette réponse suppose les mêmes conventions que dans la question, c'est-à-dire que les masters sont dans z:\masters
et les JPG sont dans z:\jpgs
. Le dossier de destination est supposé être z:\dest
.
Réponse courte
dir -File -Recurse z:\masters\ | % { if (dir -Recurse "z:\jpgs\$($_.BaseName).jpg") { mkdir -ErrorAction SilentlyContinue $($_.Directory -replace "\\masters\\", "\dest\"); cp $_.FullName $($_.FullName -replace "\\masters\\", "\dest\")} }
Explication courte : pour chaque fichier dans z:\masters
ou l'un de ses sous-répertoires directs et indirects, il vérifie si l'un des sous-répertoires directs ou indirects de z:\jpgs
contient un fichier JPG avec le même nom de base, et si c'est le cas, il copie le fichier principal dans le dossier de destination après avoir créé les répertoires parent si nécessaire.
Vous pouvez tester cette solution en ligne. (Remarque : la version en ligne utilise /
comme séparateur de chemin au lieu de \
car elle s'exécute sur Linux, et New-Item -Type Répertoire
au lieu de mkdir
en raison d'une limitation de l'environnement d'exécution en ligne.)
Avec une indentation correcte et les alias développés
Get-ChildItem -File -Recurse z:\masters\ | ForEach-Object {
if (Get-ChildItem -Recurse "z:\jpgs\$($_.BaseName).jpg") {
mkdir -ErrorAction SilentlyContinue $($_.Directory -replace "\\masters\\", "\dest\")
Copy-Item $_.FullName $($_.FullName -replace "\\masters\\", "\dest\")
}
}
Explication détaillée
dir -File -Recurse z:\masters\
liste tous les fichiers dans z:\masters
et ses sous-répertoires (-Recurse
). dir
est un alias pour Get-ChildItem
%
est un alias pour ForEach-Object. Il exécute un script (encadré d'accolades {}
) pour chaque objet dans le pipeline.
$_
est l'objet actuel dans le pipeline (c'est-à-dire l'un des fichiers principaux).
dir -Recurse "z:\jpgs\$($_.BaseName).jpg"
renvoie tous les fichiers du répertoire z:\jpgs
et de ses sous-répertoires qui ont le même nom de base que le fichier principal actuel ($_.BaseName
) et une extension .jpg
. Il renvoie $null
s'il n'y en a pas, ce qui est ensuite converti en $false
lorsqu'il est évalué comme condition de l'instruction if
.
$_.FullName -replace "\\masters\\", "\dest\"
est le chemin complet du fichier de destination : c'est le chemin complet du fichier principal ($_.FullName
) où \masters\
est remplacé par \dest\
en utilisant l' opérateur -replace. Cela est utilisé pour avoir la même structure de répertoire dans \dest
que dans \masters
.
mkdir -ErrorAction SilentlyContinue $($_.Directory -replace "\\masters\\", "\dest\")
crée les dossiers parent du fichier de destination. Cette étape est nécessaire car Copy-Item
ne permet pas actuellement (au moment de PowerShell 6.1) de créer des répertoires parents si nécessaire et échoue lorsqu'ils sont manquants. Le paramètre -ErrorAction SilentlyContinue
empêche la commande mkdir
d'échouer si les répertoires parent existent déjà.
Copy-Item $_.FullName $($_.FullName -replace "\\masters\\", "\dest\")
copie le fichier principal dans le dossier de destination.
Remarques
Dans l'expression $_.FullName -replace "\\masters\\", "\dest\"
, les barres obliques inverses (\
) sont doublées dans le premier paramètre de -replace
mais pas dans le second. Cela est dû au fait que le premier paramètre de l' opérateur -replace est une expression régulière dans laquelle une barre oblique inverse est un caractère spécial qui doit être échappé, tandis que la barre oblique inverse n'est pas un caractère spécial dans la chaîne de remplacement (le deuxième paramètre).