Search code examples
powershellcopy-item

Powershell copy only selected files with folder structure


I have a folder hierarchy with a lot of files. I need to copy all folders and only selected files. For this purposes I write script:

$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" }
$Destination = "D:\test\"
Copy-Item $files -Destination $Destination -recurse

When I execute variable $files, it returns correct path:

enter image description here

But when I execute Copy-Item it returns not full path:

enter image description here

Perhaps my approach is wrong. If so, how to copy entire folder structure, and only selected files (in this case system.serviceModel.client.config file)?

UPD1 Ok, I've found, how to copy only folders:

$path = "D:\Drop\SOA-ConfigurationManagement - Test\181\"
$Destination = "D:\test\"
Copy-Item $path $Destination -Filter {PSIsContainer} -Recurse -Force

But how to copy only selected files, preserving their location? What needs to be in $Destination variable?

$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" } | % { Copy-Item -Path $_.FullName -Destination $Destination }

Solution

  • To copy the whole folder structure AND files with a certain name, below code should do what you want:

    $Source      = 'D:\Drop\SOA-ConfigurationManagement - Test\181'
    $Destination = 'D:\test'
    $FileToCopy  = 'system.serviceModel.client.config'
    
    # loop through the source folder recursively and return both files and folders
    Get-ChildItem -Path $Source -Recurse | ForEach-Object {
        if ($_.PSIsContainer) {
            # if it's a folder, create the new path from the FullName property
            $targetFolder = Join-Path -Path $Destination -ChildPath $_.FullName.Substring($Source.Length)
            $copyFile = $false
        }
        else {
            # if it's a file, create the new path from the DirectoryName property
            $targetFolder = Join-Path -Path $Destination -ChildPath $_.DirectoryName.Substring($Source.Length)
            # should we copy this file? ($true or $false)
            $copyFile = ($_.Name -like "*$FileToCopy*")
        }
        # create the target folder if this does not exist
        if (!(Test-Path -Path $targetFolder -PathType Container)) {
            $null = New-Item -Path $targetFolder -ItemType Directory
        }
        if ($copyFile) {
            $_ | Copy-Item -Destination $targetFolder -Force
        }
    }