2009-04-08 3 views
57

Je l'extrait de code de script suivante PowerShell:liste d'exclusion dans PowerShell Copy-Item ne semble pas fonctionner

$source = 'd:\t1\*' 
$dest = 'd:\t2' 
$exclude = @('*.pdb','*.config') 
Copy-Item $source $dest -Recurse -Force -Exclude $exclude 

Ce qui fonctionne pour copier tous les fichiers et dossiers de t1 à t2, mais il exclut seulement exclure la liste dans le dossier "racine"/"premier niveau" et non dans les sous-dossiers. Comment puis-je exclure la liste d'exclusion dans tous les dossiers?

Répondre

80

Je pense que le meilleur moyen est d'utiliser Get-ChildItem et pipe dans la commande Copy-Item.

Je trouve que cela a fonctionné:

$source = 'd:\t1' 
$dest = 'd:\t2' 
$exclude = @('*.pdb','*.config') 
Get-ChildItem $source -Recurse -Exclude $exclude | Copy-Item -Destination {Join-Path $dest $_.FullName.Substring($source.length)} 

Fondamentalement, ce qui se passe ici est que vous allez à travers les fichiers valides, un par un, puis de les copier sur le nouveau chemin. L'instruction 'Join-Path' à la fin est telle que les répertoires sont également conservés lors de la copie des fichiers. Cette partie prend le répertoire de destination et le joint au répertoire après le chemin source.

J'ai eu l'idée de here, puis l'ai modifié un peu pour que cela fonctionne pour cet exemple.

J'espère que cela fonctionne!

+1

C'était la solution que je vais poster. :) J'ai trouvé que -Inclure et -Exclude ne fonctionne pas correctement pour Select-String, Copy-Item, et d'autres commandes. Cela fonctionne correctement pour Get-ChildItem (dir). – JasonMArcher

+0

IIRC, si vous regardez le fichier d'aide pour ces cmdlets, ils indiquent que ces paramètres ne fonctionnent pas comme prévu. Pour expédier est de choisir. –

+0

Merci Aaron - qui a fonctionné parfaitement! – Guy

1

Je cherchais un moyen de copier des fichiers modifiés après une certaine date/horodatage afin de les archiver. De cette façon, je pourrais enregistrer exactement sur quels fichiers j'ai travaillé (en supposant que je sais quand j'ai commencé). (Oui, je sais que c'est ce que SCM est, mais il y a des moments où je veux juste prendre un instantané de mon travail sans le vérifier.)

En utilisant l'astuce du landyman, et d'autres choses que j'ai trouvées ailleurs, j'ai trouvé que cela fonctionnait:

$source = 'c:\tmp\foo' 
$dest = 'c:\temp\foo' 
$exclude = @('*.pdb', '*.config') 
Get-ChildItem $source -Recurse -Exclude $exclude | 
    where-object {$_.lastwritetime -gt "8/24/2011 10:26 pm"} | 
    Copy-Item -Destination {Join-Path $dest $_.FullName.Substring($source.length)} 
5

Le paramètre exclude ne fonctionnera pas avec dirs. Une variante du script de Bo fait l'affaire:

$source = 'c:\tmp\foo' 
$dest = 'c:\temp\foo' 
$exclude = '\.bak' 
Get-ChildItem $source -Recurse | where {$_.FullName -notmatch $exclude} | 
    Copy-Item -Destination {Join-Path $dest $_.FullName.Substring($source.length)} 
+1

Votre clause "where" va en fait à "notmatch" sur le Expression régulière ".bak", c'est-à-dire "[n'importe quel caractère] bak". Votre regex doit être '\ .bak', ou vous devez utiliser '-notlike' si vous voulez une chaîne de caractères droite. –

0

J'ai eu un problème similaire en l'étendant un peu. Je veux une solution fonctionnant pour des sources comme

$source = "D:\scripts\*.sql" 

aussi. J'ai trouvé cette solution:

function Copy-ToCreateFolder 
{ 
    param(
     [string]$src, 
     [string]$dest, 
     $exclude, 
     [switch]$Recurse 
    ) 

    # The problem with Copy-Item -Rec -Exclude is that -exclude effects only top-level files 
    # Copy-Item $src $dest -Exclude $exclude  -EA silentlycontinue -Recurse:$recurse 
    # http://stackoverflow.com/questions/731752/exclude-list-in-powershell-copy-item-does-not-appear-to-be-working 

    if (Test-Path($src)) 
    { 
     # Nonstandard: I create destination directories on the fly 
     [void](New-Item $dest -itemtype directory -EA silentlycontinue) 
     Get-ChildItem -Path $src -Force -exclude $exclude | % { 

      if ($_.psIsContainer) 
      { 
       if ($Recurse) # Non-standard: I don't want to copy empty directories 
       { 
        $sub = $_ 
        $p = Split-path $sub 
        $currentfolder = Split-Path $sub -leaf 
        #Get-ChildItem $_ -rec -name -exclude $exclude -Force | % { "{0} {1}" -f $p, "$currentfolder\$_" } 
        [void](New-item $dest\$currentfolder -type directory -ea silentlycontinue) 
        Get-ChildItem $_ -Recurse:$Recurse -name -exclude $exclude -Force | % { Copy-item $sub\$_ $dest\$currentfolder\$_ } 
       } 
      } 
      else 
      { 

       #"{0} {1}" -f (split-path $_.fullname), (split-path $_.fullname -leaf) 
       Copy-Item $_ $dest 
      } 
     } 
    } 
} 
+0

Il existe des problèmes avec le dernier élément de copie qui ne fonctionne pas si vous êtes dans un dossier imbriqué. –

4

Comme le code de format de commentaires mal je posterai comme réponse mais c'est juste un ajout à la réponse de @ landyman. Le script proposé présente un inconvénient: il crée des dossiers à double imbrication. Par exemple pour 'd: \ t1 \ sub1', il créera le répertoire vide 'd: \ t2 \ sub1 \ sub1'. Cela est dû au fait que Copy-Item pour les répertoires attend le nom du répertoire parent dans la propriété -Destination pas le nom du répertoire lui-même. Voilà une solution que j'ai trouvé:

Get-ChildItem -Path $from -Recurse -Exclude $exclude | 
     Copy-Item -Force -Destination { 
     if ($_.GetType() -eq [System.IO.FileInfo]) { 
      Join-Path $to $_.FullName.Substring($from.length) 
     } else { 
      Join-Path $to $_.Parent.FullName.Substring($from.length) 
     } 
     } 
+1

Merci! Je suis tombé sur ce problème exactement. – ferventcoder

1

Get-ChildItem avec Join-chemin travaillait surtout pour moi, mais je l'ai réalisé copiait les répertoires racine dans les autres répertoires racine, ce qui était mauvais.

Par exemple

  • c: \ unDossier
  • c: \ unDossier \ CopyInHere
  • c: \ unDossier \ CopyInHere \ Thing.txt
  • c: \ unDossier \ CopyInHere \ SubFolder
  • c: \ unDossier \ CopyInHere \ SubFolder \ Thin2.txt

  • Source Répertoire: c: \ UnDossier \ CopyInHere

  • Destination Directory: d: \ PutItInHere

Objectif: Copier tous les childitem intérieur c: \ unDossier \ CopyInHere à la racine d: \ PutItInHere, mais non compris c: \ unDossier \ CopyInHere lui-même.
- E.g. Les enfants de CopyInHere et les enfants de PutItInHere

Les exemples ci-dessus le font la plupart du temps, mais ce qui se passe est qu'il crée un dossier appelé sous-dossier, et crée un dossier dans le dossier appelé sous-dossier. Cela est dû au fait que Join-Path calcule un chemin de destination de d: \ PutItInHere \ SubFolder pour l'élément enfant SubFolder, de sorte que SubFolder get est créé dans un dossier appelé SubFolder. J'ai contourné cela en utilisant Get-ChildItems pour ramener une collection d'éléments, puis en utilisant une boucle pour la parcourir. Cela fait en somme, le nom complet de l'élément actuel pour le calcul de la destination. Cependant, il vérifie ensuite s'il s'agit d'un objet DirectoryInfo. Si elle vérifie si c'est Parent Directory est le répertoire source, cela signifie que le dossier en cours d'itération est un enfant direct du répertoire source, en tant que tel nous ne devrions pas ajouter son nom au répertoire de destination, car nous voulons que ce dossier soit créé dans le répertoire de destination, pas dans un dossier de celui-ci dans le répertoire de destination.

Ensuite, tous les autres dossiers fonctionneront correctement.

1
$sourcePath="I:\MSSQL\Backup\Full" 
[email protected]("MASTER", "DBA", "MODEL", "MSDB") 
$sourceFiles=(ls $sourcePath -recurse -file) | where-object { $_.directory.name -notin $excludedFiles } 

C'est ce que j'ai fait, j'avais besoin de copier un tas de fichiers de sauvegarde à un emplacement distinct sur le réseau pour le ramassage du client. nous ne voulions pas qu'ils aient les sauvegardes de base de données système ci-dessus.

4

J'ai eu ce problème, aussi, et j'ai passé 20 minutes à appliquer les solutions ici, mais j'ai continué à avoir des problèmes.
J'ai donc choisi d'utiliser robocopy - OK, ce n'est pas PowerShell, mais devrait être disponible partout où powershell fonctionne.

Et cela a fonctionné dès la sortie de la boîte:

robocopy $source $dest /S /XF <file patterns to exclude> /XD <directory patterns to exclude> 

par exemple

robocopy $source $dest /S /XF *.csproj /XD obj Properties Controllers Models 

De plus, il a des tonnes de fonctionnalités, comme la copie reprenable. Docs here.

+1

'robocopy' est aussi mon préféré, mais faites attention si votre script repose sur le code de sortie. Voir https://blogs.technet.microsoft.com/deploymentguys/2008/06/16/robocopy-exit-codes/ – SBF

0

L'extrait de la question fonctionne maintenant comme prévu dans PowerShell 5.

Questions connexes