Because the -Exclude parameter of Get-ChildItem is not filtering on subfolders when using the -Recurse flag, see among other unable-to-exclude-directory-using-get-childitem-exclude-parameter-in- powershell
But the -Exclude parameter can be used to filter out folders on the root level
I wrote my own recursive function:
function Get-ChildItem-Recurse() {
[cmdletbinding()]
Param(
[parameter(ValueFromPipelineByPropertyName = $true)]
[alias('FullName')]
[string[]] $Path,
[string] $Filter,
[string[]] $Exclude,
[string[]] $Include,
[switch] $Recurse = $true,
[switch] $File = $false
)
Process {
ForEach ( $P in $Path ) {
Get-ChildItem -Path $P -Filter $Filter -Include $Include -Exclude $Exclude | ForEach-Object {
if ( -not ( $File -and $_.PSIsContainer ) ) {
$_
}
if ( $Recurse -and $_.PSIsContainer ) {
$_ | Get-ChildItem-Recurse -Filter $Filter -Exclude $Exclude -Include $Include -Recurse:$Recurse
}
}
}
}
}
When I pipe the result to a ForEach-Object to Copy the result to a different destination, all works fine, and the items except those that match the exclude parameter are copied
$source = 'D:\Temp\'
$destination = 'D:\Temp_Copy\'
Get-ChildItem-Recurse -Path $source -Exclude @( '*NotThis*', '*NotThat*' ) | ForEach-Object {
$_ | Copy-Item -Destination ( "$($destination)$($_.FullName.Substring($source.Length))" ) -Force
}
When I pipe it directly to the Copy-Item commandlet, I get a null-valued error, because .Substring() is called on $_.FullName that is apparently null
Get-ChildItem-Recurse -Path $source -Exclude @( '*NotThis*', '*NotThat*' ) |
Copy-Item -Destination ( "$($destination)$($_.FullName.Substring($source.Length))" ) -Force
Because the native commandlet Get-ChildItem does allow me to pipe its result to Copy-Item, I like my own custom function also to be able to do that. But I can't figure out why it's not working.
Use a scriptblock to dynamically bind a piped input value to the parameter:
Get-ChildItem ... |Copy-Item -Destination { "$($destination)$($_.FullName.Substring($source.Length))" }
The following answer by mklement0 has extensive details on this kind of dynamic binding (retroactively named "delay-bind scriptblocks", or colloquially "pipeline-bound scriptblocks"):
For PowerShell cmdlets, can I always pass a script block to a string parameter?