Development Tip

PowerShell Copy-Item의 제외 목록이 작동하지 않는 것 같습니다.

yourdevel 2020. 11. 27. 21:29
반응형

PowerShell Copy-Item의 제외 목록이 작동하지 않는 것 같습니다.


다음과 같은 PowerShell 스크립트 스 니펫이 있습니다.

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

모든 파일과 폴더를 t1에서 t2로 복사하는 데 사용할 수 있지만 "root"/ "first-level"폴더의 제외 목록 만 제외하고 하위 폴더는 제외합니다.

모든 폴더에서 제외 목록을 제외하려면 어떻게해야합니까?


가장 좋은 방법은 Copy-Item 명령에서 Get-ChildItem 및 파이프를 사용하는 것입니다.

이것이 효과가 있음을 발견했습니다.

$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'문은 파일을 복사 할 때 디렉토리도 유지되도록하는 것입니다. 이 부분은 대상 디렉토리를 가져 와서 소스 경로 뒤의 디렉토리와 결합합니다.

여기 에서 아이디어를 얻은 다음이 예제에서 작동하도록 약간 수정했습니다.

나는 그것이 효과가 있기를 바랍니다!


나도이 문제가 있었고 여기에 솔루션을 적용하는 데 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

또한 재개 가능한 사본과 같은 수많은 기능이 있습니다. 여기에 문서가 있습니다 .


댓글 형식 코드로 나는 답변으로 게시 할 것이지만 @landyman의 답변에 추가되었습니다. 제안 된 스크립트에는 단점이 있습니다. 이중 중첩 폴더를 생성합니다. 예를 들어 '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)
  }
}

exclude 매개 변수는 dirs에서 작동하지 않습니다. Bo의 스크립트 변형이 트릭을 수행합니다.

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

구문 사양은 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>]

배열 생성에서 명시 적이 지 않은 경우 Object []로 끝납니다. 이는 대부분의 경우 무시되어 형식 안전성 때문에 "버기 동작"의 모양을 남깁니다. PowerShell은 스크립트 블록을 처리 할 수 ​​있기 때문에 유형별 변수 이외의 평가 (유효한 문자열을 결정할 수 있음)는 실행 정책이 느슨한 시스템에 대한 주입 모드 공격의 가능성을 열어줍니다.

따라서 이것은 신뢰할 수 없습니다.

PS > $omissions = @("*.iso","*.pdf","*.zip","*.msi")
PS > $omissions.GetType()

Note the result....
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

그리고 이것은 작동합니다 .... 예 :

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

"단일"요소조차도 1 요소 배열을 만들기 위해 동일한 캐스트가 필요합니다.

집에서이 작업을 시도하는 경우 위의 예제에서 다시 캐스팅하기 전에 Replace-Variable "누락"을 사용하여 $ omissions의 존재를 정리하십시오.

그리고 내가 테스트 한 안정적으로 작동하는 파이프 라인에 관해서는 ....

--------------------------------------------------------------------------------------- cd $sourcelocation

ls | ?{$_ -ne $null} | ?{$_.BaseName -notmatch "^\.$"} | %{$_.Name} | cp -Destination $targetDir -Exclude $omissions -recurse -ErrorAction silentlycontinue 
---------------------------------------------------------------------------------------

위는 기본 ( "현재"선택한) 디렉토리에있는 소스 파일의 디렉토리 목록을 수행하고, 잠재적 인 문제 항목을 필터링하고, 파일을 기본 이름으로 변환하고, cp (복사 항목 별칭)가 파일에 "by name "을"현재 디렉터리 "에 추가하여 파일 개체를 다시 가져 와서 복사합니다. 이것은 제외 된 파일을 포함 할 수도있는 디렉토리를 포함하여 빈 디렉토리를 생성합니다 (물론 제외 항목은 제외). 또한 "ls"(get-childitem)는 cp에 남아있는 재귀가 아닙니다. 마지막으로-문제가 있고 디버깅이 필요한 경우 -ErrorAction 조용히 계속 스위치 및 인수를 제거하여 그렇지 않으면 스크립트를 중단 할 수있는 많은 성가신 요소를 숨 깁니다.

주석이 "\"포함과 관련된 사람들의 경우, 인터프리터 (예 : PowerShell)를 통해 .NET 하위 계층에 대해 작업하고 있으며 예를 들어 C #에서는 단일 "\"( 또는 문자열에 여러 개의 싱글이있는 경우) 컴파일러에서 "\\"를 사용하여 백 슬래시를 이스케이프하거나 @ "\"에서와 같이 문자열 앞에 @를 사용하여 조건을 수정하도록 요구합니다. 나머지 다른 옵션은 '\'와 같이 작은 따옴표로 묶인 문자열의 엔클로저입니다. 이 모든 것은 "\ n"등과 같은 문자 조합의 ASCII 보간 때문입니다.

후자는 훨씬 더 큰 주제이므로 고려해 보겠습니다.


특정 날짜 / 타임 스탬프 이후에 수정 된 파일을 복사하여 보관하는 방법을 찾고있었습니다. 이렇게하면 작업 한 파일을 정확히 저장할 수 있습니다 (시작시기를 알고 있다고 가정). (예, 이것이 SCM의 용도라는 것을 알고 있지만 체크인하지 않고 내 작업을 스냅 샷하고 싶을 때가 있습니다.)

landyman의 팁과 다른 곳에서 찾은 물건을 사용하여 이것이 효과가 있음을 알았습니다.

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

Join-Path를 사용하는 Get-ChildItem은 대부분 저에게 효과적이지만 다른 루트 디렉터리 내부의 루트 디렉터리를 복사하는 것이 나쁘다는 것을 깨달았습니다.

예를 들면

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

  • 소스 디렉토리 : c : \ SomeFolder \ CopyInHere

  • 대상 디렉토리 : d : \ PutItInHere

Goal: Copy every childitem Inside c:\SomeFolder\CopyInHere to the root of d:\PutItInHere, but not including c:\SomeFolder\CopyInHere itself.
- E.g. Take all the children of CopyInHere and make them Children of PutItInHere

The above examples do this most of the way, but what happens is It Creates a folder Called SubFolder, and Creates a Folder in Folder called SubFolder.

That's because Join-Path Calculates a destination path of d:\PutItInHere\SubFolder for the SubFolder child item, so SubFolder get's created in a Folder called SubFolder.

I got around this by Using Get-ChildItems to bring back a collection of the items, then using a loop to go through it.

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
}

What this does in short, is it uses the current item's full name for the destination calculation. However it then checks to see if it is a DirectoryInfo object. If it is it checks if it's Parent Folder is the Source Directory, that means the current folder being iterated is a direct child of the source directory, as such we should not append it's name to the destination directory, because we want that folder to be created in the destination directory, not in a folder of it's in the destination directory.

Following that, every other folder will work fine.


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

this is what i did, i needed to copy out a bunch of backup files to a separate location on the network for client pickup. we didn't want them to have the above system DB backups.


I had a similar problem extending this a bit. I want a solution working for sources like

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

too. I found this solution:

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

참고URL : https://stackoverflow.com/questions/731752/exclude-list-in-powershell-copy-item-does-not-appear-to-be-working

반응형