Ich habe den folgenden Ausschnitt aus dem PowerShell-Skript:
$source = 'd:\t1\*'
$dest = 'd:\t2'
$exclude = @('*.pdb','*.config')
Copy-Item $source $dest -Recurse -Force -Exclude $exclude
Dies funktioniert, um alle Dateien und Ordner von t1 nach t2 zu kopieren, schließt jedoch nur die Ausschlussliste im Ordner "root" / "first-level" und nicht in Unterordnern aus.
Wie kann ich die Ausschlussliste in allen Ordnern ausschließen?
quelle
Ich hatte auch dieses Problem und verbrachte 20 Minuten damit, die Lösungen hier anzuwenden, hatte aber weiterhin Probleme.
Also habe ich mich für Robocopy entschieden - OK, es ist keine Powershell, sollte aber überall dort verfügbar sein, wo Powershell läuft.
Und es hat sofort funktioniert:
robocopy $source $dest /S /XF <file patterns to exclude> /XD <directory patterns to exclude>
z.B
robocopy $source $dest /S /XF *.csproj /XD obj Properties Controllers Models
Außerdem verfügt es über unzählige Funktionen, wie z. B. eine wiederaufnehmbare Kopie. Docs hier .
quelle
robocopy
ist auch mein Favorit, aber pass auf, ob dein Skript auf dem Exit-Code basiert. Siehe blogs.technet.microsoft.com/deploymentguys/2008/06/16/…Als Kommentar formatieren Code schlecht, ich werde als Antwort posten, aber es ist nur eine Ergänzung zu @ landymans Antwort. Das vorgeschlagene Skript hat einen Nachteil: Es werden doppelt verschachtelte Ordner erstellt. Zum Beispiel wird für 'd: \ t1 \ sub1' ein leeres Verzeichnis 'd: \ t2 \ sub1 \ sub1' erstellt. Dies liegt an der Tatsache, dass Copy-Item für Verzeichnisse den Namen des übergeordneten Verzeichnisses in der Eigenschaft -Destination und nicht den Verzeichnisnamen selbst erwartet. Hier ist eine Problemumgehung, die ich gefunden habe:
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) } }
quelle
Der Parameter exclude funktioniert nicht mit Verzeichnissen. Eine Variante von Bo's Skript macht den Trick:
$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)}
quelle
Beachten Sie, dass die Syntaxspezifikation einen STRING ARRAY erfordert. 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>]
Wenn Sie in Ihrer Array-Generierung nicht explizit sind, erhalten Sie ein Objekt [] - und das wird in vielen Fällen ignoriert, sodass aufgrund der Typensicherheit das Erscheinungsbild eines "fehlerhaften Verhaltens" auftritt. Da PowerShell Skriptblöcke verarbeiten kann, würde die Auswertung einer anderen als einer typspezifischen Variablen (damit eine gültige Zeichenfolge ermittelt werden kann) eine Öffnung für das Potenzial eines Angriffs im Injektionsmodus auf jedes System hinterlassen, dessen Ausführungsrichtlinie lax war.
Das ist also unzuverlässig:
PS > $omissions = @("*.iso","*.pdf","*.zip","*.msi") PS > $omissions.GetType() Note the result.... IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array
Und das funktioniert ... zum Beispiel:
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
Beachten Sie, dass selbst ein "einzelnes" Element immer noch dieselbe Umwandlung erfordert, um ein Array mit 1 Element zu erstellen.
Wenn Sie dies zu Hause versuchen, stellen Sie sicher, dass Sie die "Auslassungen" der Ersetzungsvariablen verwenden, um das Vorhandensein von $ Auslassungen zu beseitigen, bevor Sie sie in den oben gezeigten Beispielen neu zusammenstellen.
Und was eine Pipeline betrifft, die zuverlässig funktioniert, die ich getestet habe ...
--------------------------------------------------------------------------------------- cd $sourcelocation ls | ?{$_ -ne $null} | ?{$_.BaseName -notmatch "^\.$"} | %{$_.Name} | cp -Destination $targetDir -Exclude $omissions -recurse -ErrorAction silentlycontinue ---------------------------------------------------------------------------------------
Oben wird eine Verzeichnisliste der Quelldateien im Basisverzeichnis (ausgewählt "aktuell") erstellt, potenzielle Problemelemente herausgefiltert, die Datei in den Basisnamen konvertiert und cp (Alias für Kopierelemente) gezwungen, erneut auf die Datei zuzugreifen name "im" aktuellen Verzeichnis "- so wird das Dateiobjekt erneut abgerufen und kopiert. Dadurch werden leere Verzeichnisse erstellt, einschließlich solcher, die möglicherweise sogar ausgeschlossene Dateien enthalten (natürlich abzüglich der Ausschlüsse). Beachten Sie auch, dass "ls" (get-childitem) NICHT -recurse ist - das bleibt cp überlassen. Schließlich - wenn Sie Probleme haben und debuggen müssen, entfernen Sie den Schalter -ErrorAction Silentcontinue und das Argument, das viele Belästigungen verbirgt, die das Skript andernfalls unterbrechen könnten.
Beachten Sie für diejenigen, deren Kommentare sich auf "\" -Einschlüsse beziehen, dass Sie über die .NET-Unterschicht über einen Interpreter (z. B. PowerShell) arbeiten und in c # beispielsweise ein einzelnes "\" ( oder mehrere Singles in einer Zeichenfolge), führt der Compiler dazu, dass Sie die Bedingung korrigieren müssen, indem Sie entweder "\\" verwenden, um dem Backslash zu entkommen, oder der Zeichenfolge ein @ wie in @ "\" voranstellen. Die andere verbleibende Option ist das Einschließen der Zeichenfolge in einfache Anführungszeichen als '\'. All dies ist auf die ASCII-Interpolation von Zeichenkombinationen wie "\ n" usw. zurückzuführen.
Letzteres ist ein viel größeres Thema, daher überlasse ich Ihnen diese Überlegung.
quelle
Ich suchte nach einer Möglichkeit, Dateien zu kopieren, die nach einem bestimmten Datum / Zeitstempel geändert wurden, um sie zu archivieren. Auf diese Weise konnte ich genau speichern, an welchen Dateien ich gearbeitet habe (vorausgesetzt, ich weiß, wann ich angefangen habe). (Ja, ich weiß, dafür ist SCM gedacht, aber manchmal möchte ich nur einen Schnappschuss meiner Arbeit machen, ohne sie einzuchecken.)
Mit Landyman's Tipp und Sachen, die ich woanders gefunden habe, stellte ich fest, dass dies funktionierte:
$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)}
quelle
Get-ChildItem mit Join-Path funktionierte hauptsächlich für mich, aber mir wurde klar, dass Root-Verzeichnisse in die anderen Root-Verzeichnisse kopiert wurden, was schlecht war.
Zum Beispiel
c: \ SomeFolder \ CopyInHere \ SubFolder \ Thin2.txt
Quellverzeichnis: c: \ SomeFolder \ CopyInHere
Ziel: Kopieren Sie jedes untergeordnete Element in c: \ SomeFolder \ CopyInHere in das Stammverzeichnis von d: \ PutItInHere, jedoch ohne c: \ SomeFolder \ CopyInHere selbst.
- Nehmen Sie zB alle Kinder von CopyInHere und machen Sie sie zu Kindern von PutItInHere
Die obigen Beispiele tun dies meistens, aber es wird ein Ordner namens SubFolder erstellt und ein Ordner im Ordner SubFolder erstellt.
Dies liegt daran, dass Join-Path einen Zielpfad von d: \ PutItInHere \ SubFolder für das untergeordnete Element SubFolder berechnet, sodass SubFolder in einem Ordner namens SubFolder erstellt wird.
Ich habe dies umgangen, indem ich Get-ChildItems verwendet habe, um eine Sammlung der Elemente zurückzubringen, und dann eine Schleife verwendet habe, um sie zu durchlaufen.
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 }
Kurz gesagt, es wird der vollständige Name des aktuellen Elements für die Zielberechnung verwendet. Anschließend wird jedoch überprüft, ob es sich um ein DirectoryInfo-Objekt handelt. Wenn dies der Fall ist, wird überprüft, ob der übergeordnete Ordner das Quellverzeichnis ist. Dies bedeutet, dass der aktuell iterierte Ordner ein direktes untergeordnetes Element des Quellverzeichnisses ist. Daher sollten wir seinen Namen nicht an das Zielverzeichnis anhängen, da dieser Ordner sein soll Erstellt im Zielverzeichnis, nicht in einem Ordner, der sich im Zielverzeichnis befindet.
Danach funktioniert jeder andere Ordner einwandfrei.
quelle
$sourcePath="I:\MSSQL\Backup\Full" $excludedFiles=@("MASTER", "DBA", "MODEL", "MSDB") $sourceFiles=(ls $sourcePath -recurse -file) | where-object { $_.directory.name -notin $excludedFiles }
Dies ist, was ich getan habe, ich musste eine Reihe von Sicherungsdateien an einen separaten Ort im Netzwerk für die Client-Abholung kopieren. Wir wollten nicht, dass sie die oben genannten System-DB-Backups haben.
quelle
Ich hatte ein ähnliches Problem, das ein bisschen erweitert wurde. Ich möchte eine Lösung für Quellen wie
$source = "D:\scripts\*.sql"
auch. Ich habe diese Lösung gefunden:
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 } } } }
quelle
Das folgende Snippet kopiert alle Dateien und Ordner von
$source
nach$dest
, mit Ausnahme von.pdb
und.config
Dateien aus dem Stammordner und den Unterordnern:Get-ChildItem -Path $source | Copy-Item -Destination $dest -Recurse -Container -Exclude @('*.pdb','*.config')
quelle