Исключить список в PowerShell Copy-Item не работает

У меня есть следующий фрагмент PowerShell script:

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

Что работает, чтобы скопировать все файлы и папки с t1 на t2, но это исключает список исключений в папке "root" / "first-level", а не в подпапках.

Как я могу исключить список исключений во все папки?

Ответ 1

Я думаю, что лучший способ - использовать Get-ChildItem и pipe в команде Copy-Item.

Я обнаружил, что это сработало:

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

В основном, здесь происходит то, что вы просматриваете действительные файлы один за другим, а затем копируете их на новый путь. Операция "Join-Path" в конце состоит в том, что каталоги также сохраняются при копировании по файлам. Эта часть принимает целевой каталог и соединяет его с каталогом по пути источника.

У меня появилась идея здесь, а затем немного изменила ее, чтобы она работала для этого примера.

Надеюсь, это сработает!

Ответ 2

Параметр exclude не будет работать с dirs. Вариант Bo script делает трюк:

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

Ответ 3

Как код формата комментариев плохо, я отправлю в качестве ответа, но это просто дополнение к ответу @landyman. Предлагаемый script имеет недостаток - он создаст двойные вложенные папки. Например, для 'd:\t1\sub1' он создаст пустую директорию 'd:\t2\sub1\sub1'. Это связано с тем, что Copy-Item для каталогов ожидает, что имя родительского каталога в свойстве -Destination не является именем каталога. Вот обходной путь, который я нашел:

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

Ответ 4

У меня тоже была эта проблема, и я потратил 20 минут на использование решений здесь, но у меня были проблемы.
Итак, я решил использовать robocopy - хорошо, это не powershell, но должно быть доступно везде, где работает powershell.

И он работал прямо из коробки:

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

например.

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

Кроме того, у него есть множество функций, таких как возобновляемая копия. Docs здесь.

Ответ 5

Я искал способ скопировать файлы, измененные после определенной даты/времени, чтобы их архивировать. Таким образом, я мог бы сэкономить именно те файлы, на которых я работал (при условии, что я знаю, когда начал). (Да, я знаю, для чего нужен SCM, но бывают случаи, когда я просто хочу сделать снимок моей работы, не проверяя ее.)

Используя кончик землянина, и прочее, которое я нашел в другом месте, я обнаружил, что это сработало:

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

Ответ 6

Get-ChildItem с Join-Path работал в основном для меня, но я понял, что это копирование корневых каталогов внутри других корневых каталогов, что было плохо.

Например

  • C:\SomeFolder
  • C:\SomeFolder\CopyInHere
  • C:\SomeFolder\CopyInHere\Thing.txt
  • C:\SomeFolder\CopyInHere\подпапка
  • с:\SomeFolder\CopyInHere\подпапка\Thin2.txt

  • Исходный каталог: c:\SomeFolder\CopyInHere

  • Каталог адресатов: d:\PutItInHere

Цель: Копирование каждого дочернего элемента Внутри c:\SomeFolder\CopyInHere в корень d:\PutItInHere, но не включая c:\SomeFolder\CopyInHere.  -. Возьмите всех детей CopyInHere и сделайте их Children of PutItInHere

Вышеприведенные примеры делают это в основном, но происходит то, что происходит. Создает папку Called SubFolder и создает папку в папке под названием SubFolder.

Это потому, что Join-Path Вычисляет путь назначения d:\PutItInHere\SubFolder для дочернего элемента SubFolder, поэтому SubFolder создается в папке под названием SubFolder.

Я обошел это, используя Get-ChildItems, чтобы вернуть коллекцию элементов, а затем использовать цикл, чтобы пройти через него.

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
}

Что это делает вкратце, использует ли оно полное имя текущего элемента для вычисления адресата. Однако затем он проверяет, является ли это объектом DirectoryInfo. Если он проверяет, является ли Parent Folder исходным каталогом, это означает, что текущая текущая папка является прямым дочерним элементом исходного каталога, поэтому мы не должны добавлять это имя в целевой каталог, потому что мы хотим, чтобы эта папка была созданный в целевом каталоге, а не в папке его в целевом каталоге.

После этого каждая другая папка будет работать нормально.

Ответ 7

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

это то, что я сделал, мне нужно было скопировать кучу файлов резервных копий в отдельное место в сети для клиентского пикапа. мы не хотели, чтобы у них были резервные копии системных БД.

Ответ 8

У меня была аналогичная проблема, расширяющая это немного. Я хочу, чтобы решение работало с такими источниками, как

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

тоже. Я нашел это решение:

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/info/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
            }
        }
    }
}