2009-04-08 4 views
57

Ho il seguente frammento di script di PowerShell:Elenco di esclusione in PowerShell Copy-Item non sembra funzionare

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

che si occupa di copiare tutti i file e le cartelle da t1 a t2, ma esclude solo la escludere l'elenco nella cartella "root"/"primo livello" e non nelle sottocartelle.

Come faccio a escludere l'elenco di esclusione in tutte le cartelle?

risposta

80

Penso che il modo migliore sia usare Get-ChildItem e pipe nel comando Copy-Item.

ho scoperto che questo ha funzionato:

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

In sostanza, ciò che sta accadendo qui è che si sta andando attraverso i file validi uno per uno, poi copiandoli il nuovo percorso. L'istruzione 'Join-Path' alla fine è tale che anche le directory vengono mantenute quando si copia sui file. Quella parte prende la directory di destinazione e la unisce alla directory dopo il percorso di origine.

Ho ricevuto l'idea da here e l'ho modificata un po 'per farlo funzionare per questo esempio.

Spero che funzioni!

+1

Quella era solo la soluzione che stavo per postare. :) Ho trovato che -Include ed -Exclude non funzionano correttamente per Select-String, Copy-Item e alcuni altri comandi. Funziona correttamente con Get-ChildItem (dir). – JasonMArcher

+0

IIRC, se si guarda il file della guida per quei cmdlet, questi parametri non funzionano come previsto. Spedire è scegliere. –

+0

Grazie Aaron - ha funzionato perfettamente! – Guy

1

Stavo cercando un modo per copiare i file modificati dopo una certa data/timestamp in modo da archiviarli. In questo modo ho potuto salvare esattamente quali file ho lavorato (supponendo che so quando ho iniziato). (Sì, so che questo è ciò che SCM è per, ma ci sono momenti in cui voglio solo fare un'istantanea del mio lavoro senza verificarlo.)

Utilizzando il suggerimento del landyman, e cose che ho trovato altrove, ho scoperto che questo ha funzionato:

$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

Il parametro exclude non funziona con dirs. Una variante dello script di Bo fa il trucco:

$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

La clausola 'where' in realtà sta per "notmatch" sul Espressione regolare ".bak", cioè "[qualsiasi carattere] bak". La tua regex deve essere '\ .bak', oppure dovresti usare '-notlike' se vuoi una corrispondenza di stringhe diritte. –

0

Ho avuto un problema simile estendendolo un po '. Voglio una soluzione che funzioni per fonti come

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

troppo. Ho trovato questa soluzione:

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

Ci sono dei problemi in cui l'ultima copia non funziona se ci si trova in una cartella annidata. –

4

Come commenti formattare male il codice inserirò come risposta ma è solo un'aggiunta alla risposta di @ landyman. Lo script proposto ha uno svantaggio: creerà cartelle a doppio nidificato. Ad esempio per 'd: \ t1 \ sub1' creerà la directory vuota 'd: \ t2 \ sub1 \ sub1'. Ciò è dovuto al fatto che Copia-Elemento per le directory si aspetta che il nome della directory padre nella proprietà -Destination non sia il nome della directory stesso. Ecco una soluzione che ho trovato:

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

Grazie! Mi sono imbattuto in questo problema esattamente. – ferventcoder

1

Get-ChildItem con Join-Path stava lavorando per lo più per me, ma ho capito che era la copia di directory principali all'interno delle altre directory principali, che era male.

Per esempio

  • c: \ SomeFolder
  • c: \ SomeFolder \ CopyInHere
  • c: \ SomeFolder \ CopyInHere \ Thing.txt
  • c: \ SomeFolder \ CopyInHere \ SubFolder
  • c: \ SomeFolder \ CopyInHere \ SubFolder \ Thin2.txt Directory

  • Fonte: c: \ SomeFolder \ CopyInHere

  • Destinazione repertorio: d: \ PutItInHere

Obiettivo: Copia ogni childitem All'interno c: \ SomeFolder \ CopyInHere alla radice del d: \ PutItInHere, esclusi i C: \ SomeFolder \ CopyInHere stesso.
- E.g. Prendi tutti i figli di CopyInHere e rendili Children of PutItInHere

Gli esempi precedenti lo fanno quasi tutto, ma ciò che accade è Crea una cartella chiamata sottocartella e crea una cartella in una cartella chiamata sottocartella.

Ecco perché Join-Path Calcola un percorso di destinazione di d: \ PutItInHere \ SubFolder per l'elemento secondario Sottocartella, quindi Sottocartella get viene creata in una cartella denominata Sottocartella.

Ho risolto questo problema utilizzando Get-ChildItems per riportare una raccolta degli elementi, quindi utilizzando un ciclo per passarci attraverso.

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 
} 

In breve, utilizza il nome completo dell'elemento corrente per il calcolo della destinazione. Tuttavia, verifica quindi se è un oggetto DirectoryInfo. Se si verifica se la cartella principale è la directory di origine, significa che la cartella corrente che viene iterata è figlia diretta della directory di origine, in quanto tale non dovremmo aggiungere il nome alla directory di destinazione, perché vogliamo che quella cartella sia creato nella directory di destinazione, non in una cartella di esso nella directory di destinazione.

In seguito, ogni altra cartella funzionerà correttamente.

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

questo è quello che ho fatto, avevo bisogno di copiare un gruppo di file di backup in una posizione separata in rete per il ritiro del cliente. non volevamo che avessero i backup del sistema di cui sopra.

4

Ho avuto questo problema e ho impiegato 20 minuti per applicare le soluzioni qui, ma ho continuato ad avere problemi.
Quindi ho scelto di usare robocopy - OK, non è PowerShell, ma dovrebbe essere disponibile ovunque sia in esecuzione PowerShell.

e ha funzionato a destra, fuori dalla scatola:

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

esempio

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

Inoltre, ha un sacco di funzioni, come la copia riprendibile. Documenti here.

+1

Anche 'robocopy' è il mio preferito, ma fai attenzione se il tuo script si basa sul codice di uscita. Vedi https://blogs.technet.microsoft.com/deploymentguys/2008/06/16/robocopy-exit-codes/ – SBF

0

Il frammento della questione ora funziona come previsto in PowerShell 5.