Exclude list in PowerShell Copy-Item does not appear to be working

Viewed 87506

I have the following snippet of PowerShell script:

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

Which works to copy all files and folders from t1 to t2, but it only excludes the exclude list in the "root"/"first-level" folder and not in sub-folders.

How do I make it exclude the exclude list in all folders?

12 Answers

I think the best way is to use Get-ChildItem and pipe in the Copy-Item command.

I found that this worked:

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

Basically, what is happening here is that you're going through the valid files one by one, then copying them to the new path. The 'Join-Path' statement at the end is so that the directories are also kept when copying over the files. That part takes the destination directory and joins it with the directory after the source path.

I got the idea from here, and then modified it a bit to make it work for this example.

I hope it works!

The below snippet will copy all files and folders from $source to $dest, excluding .pdb and .config files from the root folder and sub-folders:

Get-ChildItem -Path $source | Copy-Item -Destination $dest -Recurse -Container -Exclude @('*.pdb','*.config')

One way of copying items from one folder to another using regular expressions for exclusion:

$source = '.\source'
$destination = '.\destination'
$exclude = '.*\.pdf$|.*\.mp4$|\\folder1(\\|$)|\\folder2(\\|$)'

$itemsToCopy = Get-ChildItem $source -Recurse |
    Where-Object FullName -notmatch $exclude | Select-Object -Expand FullName

$sourceFullNameLength = (Get-Item $source).FullName.Length

foreach ($item in $itemsToCopy) {
    $relativeName = $item.Substring($sourceFullNameLength + 1)
    Copy-Item -Path $item -Destination "$destination\$relativeName"
}

I wrote this for daily use and packaged it in the script module, it maintains all the directory structure and supports wildcards:

function Copy-Folder {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [String]$FromPath,

        [Parameter(Mandatory)]
        [String]$ToPath,

        [string[]] $Exclude
    )

    if (Test-Path $FromPath -PathType Container) {
        New-Item $ToPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
        Get-ChildItem $FromPath -Force | ForEach-Object {
            # avoid the nested pipeline variable
            $item = $_
            $target_path = Join-Path $ToPath $item.Name
            if (($Exclude | ForEach-Object { $item.Name -like $_ }) -notcontains $true) {
                if (Test-Path $target_path) { Remove-Item $target_path -Recurse -Force }
                Copy-Item $item.FullName $target_path
                Copy-Folder -FromPath $item.FullName $target_path $Exclude
            }
        }
    }
}

Just call the Copy-Folder -FromPath 'fromDir' -ToPath 'destDir' -Exclude *.pdb,*.config

The -FromPath and -ToPath can be omitted,

Copy-Folder -FromPath 'fromDir destDir -Exclude *.pdb,*.config

Related