2009-04-08 18 views
57

Tengo el siguiente fragmento de script de PowerShell:lista de PowerShell Copy-Item Excluir no parece estar funcionando

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

que trabaja para copiar todos los archivos y carpetas de t1 a t2, pero solamente excluye de la excluir lista en la carpeta "raíz"/"primer nivel" y no en subcarpetas.

¿Cómo puedo hacer que excluya la lista de exclusión en todas las carpetas?

Respuesta

80

Creo que la mejor manera es usar Get-ChildItem y pipe en el comando Copy-Item.

encontré que esto funcionó:

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

Básicamente, lo que está sucediendo aquí es que usted va a través de los archivos válidos, uno por uno, a continuación, copiarlos en el nuevo camino. La declaración 'Join-Path' al final es para que los directorios también se guarden al copiar sobre los archivos. Esa parte toma el directorio de destino y lo une con el directorio después de la ruta de origen.

Obtuve la idea desde here, y luego la modifiqué un poco para que funcione en este ejemplo.

¡Espero que funcione!

+1

Eso era sólo la solución que iba a publicar. :) He encontrado que -Include y -Exclude no funcionan bien para Select-String, Copy-Item y algunos otros comandos. Funciona bien para Get-ChildItem (dir). – JasonMArcher

+0

IIRC, si mira el archivo de ayuda para esos cmdlets, dicen que esos parámetros no funcionan como se esperaba. Enviar es elegir –

+0

Gracias Aaron, ¡eso funcionó a la perfección! – Guy

1

Estaba buscando una manera de copiar los archivos modificados después de una fecha/marca de tiempo determinada para archivarlos. De esta forma podría guardar exactamente en qué archivos trabajé (asumiendo que sé cuándo comencé). (Sí, sé que esto es para lo que SCM es, pero hay ocasiones en las que solo quiero hacer una instantánea de mi trabajo sin verificarlo).

Utilizando la sugerencia de Landyman, y otras cosas que encontré en otro lado, encontré que esto funcionó:

$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

El parámetro exclude no funcionará con los directorios. Una variante de la secuencia de comandos de Bo hace el truco:

$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

Tu cláusula 'where' va a "no coincidir" en el Expresión regular ".bak", es decir, "[cualquier personaje] bak". Su expresión regular debe ser '\ .bak', o debería usar '-notlike' si quiere que coincida con la secuencia recta. –

0

Tuve un problema similar al extender esto un poco. Quiero una solución que funcione para fuentes como

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

también. Encontré esta solución:

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

Existen problemas con esto donde el último elemento de copia no funciona si se encuentra en una carpeta anidada. –

4

Como código de formato de los comentarios mal lo publicaré como respuesta, pero es solo una adición a la respuesta de @ landyman. El script propuesto tiene un inconveniente: creará carpetas anidadas dobles. Por ejemplo, para 'd: \ t1 \ sub1' creará el directorio vacío 'd: \ t2 \ sub1 \ sub1'. Esto se debe al hecho de que Copy-Item para directorios espera el nombre del directorio principal en la propiedad -Destination, no el nombre del directorio en sí. He aquí una solución que encontré:

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

¡Gracias! Me encontré con este problema exactamente. – ferventcoder

1

Get-ChildItem con Join-Path estaba trabajando sobre todo para mí, pero me di cuenta que estaba copiando los directorios raíz dentro de los otros directorios raíz, que era mala.

Por ejemplo

  • c: \ SomeFolder
  • c: \ SomeFolder \ CopyInHere
  • c: \ SomeFolder \ CopyInHere \ Thing.txt
  • c: \ SomeFolder \ CopyInHere \ Subcarpeta
  • c: \ SomeFolder \ CopyInHere \ subcarpeta \ Thin2.txt

  • Fuente directorio: c: \ SomeFolder \ CopyInHere

  • Directorio de destino: d: \ PutItInHere

Objetivo: Copiar todos los childitem Dentro c: \ SomeFolder \ CopyInHere a la raíz de d: \ PutItInHere, pero sin incluir c: \ SomeFolder \ CopyInHere sí mismo.
- P. ej. Tome todos los hijos de CopyInHere y conviértalos en Children of PutItInHere

Los ejemplos anteriores hacen esto la mayor parte del camino, pero lo que ocurre es que crea una carpeta llamada SubFolder y crea una carpeta en la carpeta denominada SubFolder.

Esto se debe a Join-Path Calcula una ruta de destino de d: \ PutItInHere \ SubFolder para el elemento secundario SubFolder, para que SubFolder se cree en una carpeta llamada SubFolder.

Me salí al paso utilizando Get-ChildItems para traer de vuelta una colección de los elementos, luego usando un bucle para revisarlo.

Param(
[Parameter(Mandatory=$True,Position=1)][string]$sourceDirectory, 
[Parameter(Mandatory=$True,Position=2)][string]$destinationDirectory 
) 
$sourceDI = [System.IO.DirectoryInfo]$sourceDirectory 
$destinationDI = [System.IO.DirectoryInfo]$destinationDirectory 
$itemsToCopy = Get-ChildItem $sourceDirectory -Recurse -Exclude @('*.cs', 'Views\Mimicry\*') 
foreach ($item in $itemsToCopy){   
    $subPath = $item.FullName.Substring($sourceDI.FullName.Length) 
$destination = Join-Path $destinationDirectory $subPath 
if ($item -is [System.IO.DirectoryInfo]){ 
    $itemDI = [System.IO.DirectoryInfo]$item 
    if ($itemDI.Parent.FullName.TrimEnd("\") -eq $sourceDI.FullName.TrimEnd("\")){  
     $destination = $destinationDI.FullName 
    } 
} 
$itemOutput = New-Object PSObject 
$itemOutput | Add-Member -Type NoteProperty -Name Source -Value $item.FullName 
$itemOutput | Add-Member -Type NoteProperty -Name Destination -Value $destination 
$itemOutput | Format-List 
Copy-Item -Path $item.FullName -Destination $destination -Force 
} 

Lo que hace en resumen, es que utiliza el nombre completo del elemento actual para el cálculo del destino. Sin embargo, luego verifica si es un objeto DirectoryInfo. Si es que comprueba si su Carpeta principal es el Directorio de origen, eso significa que la carpeta actual que se está iterando es un elemento secundario directo del directorio de origen, por lo tanto, no debemos agregar su nombre al directorio de destino, porque queremos que esa carpeta sea creado en el directorio de destino, no en una carpeta de su directorio de destino.

Después de eso, cualquier otra carpeta funcionará bien.

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

esto es lo que hice, lo necesario para copiar un montón de archivos de copia de seguridad en una ubicación separada de la red de recogida cliente. no queríamos que tuvieran las copias de seguridad del sistema anteriores.

4

Tuve este problema, también, y pasé 20 minutos aplicando las soluciones aquí, pero seguí teniendo problemas.
Así que elegí usar robocopy - OK, no es Powershell, pero debería estar disponible en todas partes donde se ejecuta PowerShell.

y funcionó a la derecha de la caja:

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

por ejemplo,

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

Además, tiene un montón de características, como la copia reanudable. Docs here.

+1

'robocopy' es mi favorito también, pero ten cuidado si tu script se basa en el código de salida. Ver https://blogs.technet.microsoft.com/deploymentguys/2008/06/16/robocopy-exit-codes/ – SBF

0

El fragmento de la pregunta ahora funciona como se espera en PowerShell 5.

Cuestiones relacionadas