with - copy large files powershell
La lista de exclusión en PowerShell Copy-Item no parece estar funcionando (10)
Tengo el siguiente fragmento de script de PowerShell:
$source = ''d:/t1/*''
$dest = ''d:/t2''
$exclude = @(''*.pdb'',''*.config'')
Copy-Item $source $dest -Recurse -Force -Exclude $exclude
Que funciona para copiar todos los archivos y carpetas de t1 a t2, pero solo excluye la lista de exclusión en la carpeta "raíz" / "primer nivel" y no en las subcarpetas.
¿Cómo hago que excluya la lista de exclusión en todas las carpetas?
Como el código de formato de los comentarios es incorrecto, lo publicaré como respuesta, pero es solo una adición a la respuesta de @ landyman. La secuencia de comandos propuesta 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í. Aquí hay una solución alternativa 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)
}
}
Creo que la mejor manera es usar Get-ChildItem y pipe en el comando Copy-Item.
Descubrí 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 está pasando por los archivos válidos uno por uno, y luego los copia en la nueva ruta. 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!
El fragmento de la pregunta ahora funciona como se esperaba en PowerShell 5.
El parámetro exclude no funcionará con los directorios. Una variante del guión de Bo es 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)}
Estaba buscando una forma 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 a veces solo quiero hacer una instantánea de mi trabajo sin verificarlo).
Utilizando la sugerencia de landyman, y 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)}
Get-ChildItem con Join-Path funcionaba principalmente para mí, pero me di cuenta de que estaba copiando directorios raíz dentro de los otros directorios raíz, lo que era malo.
Por ejemplo
- c: / SomeFolder
- c: / SomeFolder / CopyInHere
- c: / SomeFolder / CopyInHere / Thing.txt
- c: / SomeFolder / CopyInHere / SubFolder
c: / SomeFolder / CopyInHere / SubFolder / Thin2.txt
Directorio de origen: c: / SomeFolder / CopyInHere
- Directorio de destino: d: / PutItInHere
Objetivo: copiar cada elemento child Inside c: / SomeFolder / CopyInHere a la raíz de d: / PutItInHere, pero sin incluir c: / SomeFolder / CopyInHere.
- Por ejemplo, tomar todos los hijos de CopyInHere y hacerlos hijos de 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 que Join-Path Calcula una ruta de destino de d: / PutItInHere / SubFolder para el elemento secundario SubFolder, de modo que SubFolder se crea en una carpeta llamada SubFolder.
Lo solucioné usando Get-ChildItems para traer de vuelta una colección de los ítems, luego usé 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
}
En resumen, lo que hace es usar 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.
También tuve este problema 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ó de la nada:
robocopy $source $dest /S /XF <file patterns to exclude> /XD <directory patterns to exclude>
p.ej
robocopy $source $dest /S /XF *.csproj /XD obj Properties Controllers Models
Además, tiene toneladas de características, como copia reanudable. Documentos here .
Tenga en cuenta que la especificación de sintaxis llama a un STRING ARRAY; ala String []
SYNTAX
Copy-Item [[-Destination] <String>] [-Confirm] [-Container] [-Credential <PSCredential>] [-Exclude <String[]>] [-Filter <String>] [-Force] [-FromSession <PSSession>] [-Include
<String[]>] -LiteralPath <String[]> [-PassThru] [-Recurse] [-ToSession <PSSession>] [-UseTransaction] [-WhatIf] [<CommonParameters>]
Si no eres explícito en tu generación de matriz, terminas con un Objeto [] - y eso es ignorado en muchos casos, dejando la apariencia de "comportamiento defectuoso" debido a la seguridad de tipo. Dado que PowerShell puede procesar secuencias de comandos, la evaluación de una variable que no sea específica de un tipo (para que se pueda determinar una cadena válida) dejaría una oportunidad para el ataque de modo de inyección en cualquier sistema cuya política de ejecución fuera poco estricta.
Entonces esto no es confiable:
PS > $omissions = @("*.iso","*.pdf","*.zip","*.msi")
PS > $omissions.GetType()
Note the result....
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Y esto funciona ... por ejemplo:
PS > $omissions = [string[]]@("*.iso","*.pdf","*.zip","*.msi")
**or**
PS > [string[]]$omissions = ("*.iso,*.pdf,*.zip,*.msi").split('','')
PS > $omissions.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String[] System.Array
Tenga en cuenta que incluso un elemento "único" aún requeriría el mismo modelo, para crear una matriz de 1 elemento.
Si está intentando hacer esto en su casa, asegúrese de utilizar las "omisiones" Reemplazar-Variable para eliminar la existencia de $ omisiones antes de volver a convertirlo en los ejemplos que se muestran arriba.
Y en cuanto a una tubería que funciona de manera confiable que he probado ....
--------------------------------------------------------------------------------------- cd $sourcelocation
ls | ?{$_ -ne $null} | ?{$_.BaseName -notmatch "^/.$"} | %{$_.Name} | cp -Destination $targetDir -Exclude $omissions -recurse -ErrorAction silentlycontinue
---------------------------------------------------------------------------------------
Lo anterior hace una lista de directorios de los archivos fuente en el directorio base (seleccionado "actual"), filtra posibles elementos problemáticos, convierte el archivo en el nombre base y fuerza cp (alias del elemento de copia) para volver a acceder al archivo "por nombrar "en el" directorio actual "- adquiriendo así el objeto de archivo, y lo copia. Esto creará directorios vacíos, incluidos aquellos que incluso pueden contener archivos excluidos (menos las exclusiones, por supuesto). Tenga en cuenta también que "ls" (get-childitem) NO -recurse - que queda en cp. Finalmente, si tiene problemas y necesita depurar, elimine la opción -ErrorAction silenntlycontinue switch and argument, que esconde muchas molestias que de otro modo podrían interrumpir la secuencia de comandos.
Para aquellos cuyos comentarios estuvieron relacionados con inclusiones "/", tenga en cuenta que está trabajando sobre la subcapa .NET a través de un intérprete (es decir, PowerShell), y en c # por ejemplo, la inclusión de una sola "/" ( o múltiples singles en una cadena), el compilador exige que se corrija la condición utilizando "//" para escapar de la barra diagonal inversa, o antecede a la cadena con un @ como en @ "/"; con la otra opción restante siendo el envolvente de la cadena entre comillas simples, como ''/'. Todo esto se debe a la interpolación ASCII de combinaciones de caracteres como "/ n", etc.
Este último es un tema mucho más grande, así que te dejaré con esa consideración.
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://.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
}
}
}
}
$sourcePath="I:/MSSQL/Backup/Full"
$excludedFiles=@("MASTER", "DBA", "MODEL", "MSDB")
$sourceFiles=(ls $sourcePath -recurse -file) | where-object { $_.directory.name -notin $excludedFiles }
esto es lo que hice, necesitaba copiar un montón de archivos de respaldo en una ubicación separada en la red para que los recogiera el cliente. no queríamos que tuvieran las copias de seguridad del sistema anteriores.