I'm trying to copy a directory structure with a PowerShell script. I wish to exclude a certain directory.
My directory structure looks like this:
C:\
└───murks
├───murks_source
│ │ test1.txt
│ │ test2.txt
│ │
│ ├───sub
│ │ subtest1.txt
│ │ subtest2.txt
│ │ subtest3.txt
│ │
│ └───sub2
│ sub2test.txt
│
└───murks_target
Now, when I run the script, it doesn't create the subdirectory "sub2" with the subordinate files. Instead, it copies all files (also those of the "sub2" subdirectory) directly into the murks_target_directory. I don't understand this behaviour, since the "Select -ExpandProperty FullName"-part looks good so far.
Any help or hints are highly appreciated. Thanks in advance!
My script looks like this so far:
$rootFolderPath = 'C:\murks\murks_source'
$excludeDirectories = ("sub");
function Exclude-Directories
{
process
{
$allowThrough = $true
foreach ($directoryToExclude in $excludeDirectories)
{
$directoryText = "*\" + $directoryToExclude
$childText = "*\" + $directoryToExclude + "\*"
if (($_.FullName -Like $directoryText -And $_.PsIsContainer) `
-Or $_.FullName -Like $childText)
{
$allowThrough = $false
break
}
}
if ($allowThrough)
{
return $_
}
}
}
Get-ChildItem $rootFolderPath -Recurse | Exclude-Directories | Select -ExpandProperty FullName | Copy-Item -Destination C:\murks\murks_target -Force
The below custom function should do what you want:
function Copy-Path {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[string]$Source,
[Parameter(Position = 1)]
[string]$Destination,
[string[]]$ExcludeFolders = $null,
[switch]$IncludeEmptyFolders
)
$Source = $Source.TrimEnd("\")
$Destination = $Destination.TrimEnd("\")
Get-ChildItem -Path $Source -Recurse | ForEach-Object {
if ($_.PSIsContainer) {
# it's a folder
if ($ExcludeFolders.Count) {
if ($ExcludeFolders -notcontains $_.Name -and $IncludeEmptyFolders) {
# create the destination folder, even if it is empty
$target = Join-Path -Path $Destination -ChildPath $_.FullName.Substring($Source.Length)
if (!(Test-Path $target -PathType Container)) {
Write-Verbose "Create folder $target"
New-Item -ItemType Directory -Path $target | Out-Null
}
}
}
}
else {
# it's a file
$copy = $true
if ($ExcludeFolders.Count) {
# get all subdirectories in the current file path as array
$subs = $_.DirectoryName.Replace($Source,"").Trim("\").Split("\")
# check each sub folder name against the $ExcludeFolders array
foreach ($folderName in $subs) {
if ($ExcludeFolders -contains $folderName) { $copy = $false; break }
}
}
if ($copy) {
# create the destination folder if it does not exist yet
$target = Join-Path -Path $Destination -ChildPath $_.DirectoryName.Substring($Source.Length)
if (!(Test-Path $target -PathType Container)) {
Write-Verbose "Create folder $target"
New-Item -ItemType Directory -Path $target | Out-Null
}
Write-Verbose "Copy file $($_.FullName) to $target"
$_ | Copy-Item -Destination $target -Force
}
}
}
}
Once in place, use it like this:
Copy-Path -Source 'C:\murks\murks_source' -Destination 'C:\murks\murks_target' -ExcludeFolders 'sub' -Verbose
Where parameter -ExcludeFolders
can be an array of foldernames to skip.
Adding switch parameter IncludeEmptyFolders
will also create empty folders in the destination. If left out, empty folders will not be copied.
Using your example file structure, the result is
C:\murks\murks_target | test1.txt | test2.txt | \---sub2 sub2test.txt