2

I'm trying to have all the files in the folder structure copied to a folder that is also part of that structure. So the destination folder is excluded form the search. I also want to exclude any folder that has in its path ".thumbnails" but when I replace the full path in the $Skip with a wild card path such as 'D:\ZZZ_Phone_test*.thumbnails' it won't work.

Secondly, I'd like to make this more efficient if possible so the job can be finished quicker. When the script is running it is mostly the CPU working not so much the harddrive.

Thirdly, is there any way how to generate some output of what was copied, skipped, errors... and save it to a logfile?

$Source = 'D:\ZZZ_Phone_test'
$Dest = 'D:\ZZZ_Phone_test\1\1\BackUp'
$Skip = 'D:\ZZZ_Phone_test\4\.thumbnails'

Get-ChildItem $Source -Directory -Recurse | ? FullName -ne $Dest | ? FullName -ne $Skip | get-ChildItem -File | Copy-Item -Exclude `
*.0,*.1,*.nomedia,*.thumbnail,*.chck,*.crypt12,*.tmp,*.db,*.crypt1,*.ini,*.pdrproj,*.pkpass,*.dat,*.enc,*.lck,*.xml,*.json,*.LOCK,*.443,*.preference `
-Destination $Dest

.

EDIT: the following works but it will only exclude files in directories whose names end with "thumbnails" or "BackUp". If there are any directories with files inside of "thumbnails" folder they will all be processed. I'd like to define the folders to be excluded the way that even if there are subdirectories with files in a directory defined in $Skip they would not be processed.

$Source = 'D:\ZZZ_Phone_test'
$Dest   = 'D:\ZZZ_Phone_test\1\1\BackUp'
$Skip   = '*thumbnails', '*BackUp'


(Get-ChildItem -Path $Source -Directory -Recurse -Exclude $Skip).FullName |
 get-ChildItem -File |
 Copy-Item -WhatIf -Exclude `
*.0,*.1,*.nomedia,*.thumbnail,*.chck,*.crypt12,*.tmp,*.db,*.crypt1,*.ini,*.pdrproj,*.pkpass,*.dat,*.enc,*.lck,*.xml,*.json,*.LOCK,*.443,*.preference `
-Destination $Dest

2 Answers2

1

For complex filtering needs, divide et impera is often useful an approach. That is, simplify the problem in multiple steps instead of trying to write an one-liner.

Let's take a directory listing of all the files and exclude the destination directory $dest. As % (shorthand for Where-Object) parameter -notmatch expects a regular expression, the $dest path is escaped with [regex]::Escape. This needs to be done, as backslash \ is a reserved character in regular expressions. One could write the path in escaped form in the first hand, like c:\\my\\path\\to\\somewhere, but Escape does all the work needed.

Get-ChildItem $source\* -Recurse -File | ? { $_.psparentpath -notmatch [regex]::escape($dest) }

Now that we have all the files except destination, start pruning the list. Since there are lots of file extensions, let's put those on an array. Loop through the array, and remove each match from the $files array.

$excluded = @("*.0", "*.1", "*.nomedia", "*.thumbnail", "*.chck", "*.crypt12", "*.tmp", "*.db",
   "*.crypt1", "*.ini", "*.pdrproj", "*.pkpass", "*.dat", "*.enc", "*.lck", "*.xml", "*.json", 
   "*.LOCK", "*.443", "*.preference")

foreach($ex in $excluded) {
    $files = $files | ? {$_.extension -notlike $ex}
}

To remove the $skip, filter the collection again:

$files = $files | ? {$_.DirectoryName -ne $skip)

At this point, all you have is an array that contains files that are to be copied into $dest. Before copying, use -WhatIf switch to see what Copy-Item would do to be sure the copy works as intended:

$files | % { Copy-Item -WhatIf $_ $dest }

To wrap up a complete example,

$source = "c:\temp\phonetest"
$dest = "c:\temp\phonetest\1\1\BackUp"
$Skip = "c:\temp\phonetest\skipme"
# Get list of all the files
Get-ChildItem $source\* -Recurse | ? { $_.psparentpath -notmatch [regex]::escape($dest) }

# Filter by extension
$excluded = @("*.0", "*.1", "*.nomedia", "*.thumbnail", "*.chck", "*.crypt12", "*.tmp", "*.db", "*.crypt1", "*.ini", "*.pdrproj", "*.pkpass", "*.dat", "*.enc", "*.lck", "*.xml", "*.json", "*.LOCK", "*.443", "*.preference")

foreach($ex in $excluded) {
$files = $files | ? {$_.extension -notlike $ex}
}

# Skip specific dir    
$files = $files | ? {$_.DirectoryName -ne $skip)

# See what would be copied
$files | % { Copy-Item -WhatIf $_ $dest }
vonPryz
  • 22,996
  • 7
  • 54
  • 65
  • Thanks vonPryz this looks really good but could you please put it together for me? Silly to say that but I'm a basic windows user and I don't really understand the second half of the first line of your code and how it is connected to the rest. What is the output of that first line and what should the [regex] bit look like when I'm excluding the path as in my question? Thanks a lot. – ThomasHunter Aug 24 '20 at 15:35
  • That bottom part is not a [regex], it's just a list of file types. aka and array. Imagine that list was just a text file, that is being read in. Yet this [I'm a basic windows user], says you've never used PowerShell before. So, it is really best that you get ramped on it first to avoid future confusion, frustration, headaches, misconceptions, bad code, and errors, that may cause major problems for you on your computer or your enterprise. Search Youtube for videos on 'Beginning PowerShell' and 'PowerShell file and folder management'. – postanote Aug 25 '20 at 00:55
  • @ThomasHunter See the edited version with more content. – vonPryz Aug 25 '20 at 05:23
1

Try this and modify as you wish for that file exclusion section...

$Source = 'D:\Temp'
$Dest   = 'D:\Destination'
$Skip   = '*est', '*here'

<#
Always build you code one use case at a time to ensure you are getting what 
you'd expect before moving ot the next.
#>

# Get all directories off a give path
(Get-ChildItem -Path $Source -Directory -Recurse).FullName | 
Select-Object -First 5
# Results
<#
D:\Temp\AddressFiles
D:\Temp\ChildFolder
D:\Temp\est
D:\Temp\here
D:\Temp\hold
#>

# Exclude named directories
(Get-ChildItem -Path $Source -Directory -Recurse -Exclude $Skip).FullName | 
Select-Object -First 5
# Results
<#
D:\Temp\AddressFiles
D:\Temp\ChildFolder
D:\Temp\ChildFolder\New folder
D:\Temp\ChildFolder\temp
D:\Temp\hold

#>

# Or include only what you want
(Get-ChildItem -Path $Source -Directory -Recurse -Include $Skip).FullName | 
Select-Object -First 5
# Results
<#
D:\Temp\ChildFolder\New folder\est
D:\Temp\est
D:\Temp\here
#>

# Loop directories and process files, trap errors
(Get-ChildItem -Path $Source -Directory -Recurse -Exclude $Skip).FullName | 
Select-Object -First 5 | 
ForEach {
    Try
    {
        "Processing $PSItem"
        $CopyItemSplat = @{
            Path        = (Get-ChildItem -Path $PSItem -ErrorAction Stop).FullName 
            Destination = $Dest 
            Verbose     = $true
            WhatIf      = $true
        }
    }
    Catch
    {
        Write-Warning -Message 'An error was encountered.'
        $PSitem.Exception.Message
    }
}
# Results
<#
Processing D:\Temp\AddressFiles
Processing D:\Temp\ChildFolder
Processing D:\Temp\ChildFolder\New folder
Processing D:\Temp\ChildFolder\temp
WARNING: An error was encountered.
The property 'FullName' cannot be found on this object. Verify that the property exists.
Processing D:\Temp\hold
WARNING: An error was encountered.
The property 'FullName' cannot be found on this object. Verify that the property exists.
#>
postanote
  • 15,138
  • 2
  • 14
  • 25
  • -Exclude $Skip works but only to exclude the last folder in the structure. For instance if I'm excluding "3" with $Skip= '*3' in 1\2\3 it excludes folder "3". But how can I also exclude all its children? If I do $Skip= '*3\*' in 1\2\3\4 it will not exclude "4" – ThomasHunter Aug 25 '20 at 00:14
  • Whatever folder you skip, nothing below it will be processed and child processing only occurs with the -recurse parameter, and since you are skipping whatever folder, you can't pass it anything further. Also that [$Skip = 'D:\ZZZ_Phone_test\4\.thumbnails'] is not a valid folder path or should not be. If you want to skip 4 and all below it, you should only be using the UNC to 4 only, not anything else. – postanote Aug 25 '20 at 01:02
  • It is not true though. If I'm excluding "3" from 1/2/3/4 the files in "3" will be excluded but everything in "4" is still processed. Is there a way how I could exclude "3" AND everything bellow "3"? so subdirectories will not be processed? – ThomasHunter Sep 07 '20 at 14:39