I need to delete some subfolders under a folder 'ToDelete'. I am using this to do that: (both should do the same deletion). my problem is that there is other folder called 'Do_Not_Copy' under ToDelete folder that contain also a folder called 'Tools' that should not be deleted. how I can protect this 'Tools' subfolder? -Exclude doesn't work.
My workaround for now is to use Rename-Item for the Tools folder
$DestinationFolder = "\\google\server\ToDelete"
Remove-Item -Path $DestinationFolder\ -Include "Tools", "Support", "Installer", "GUI", "Filer", "Documentation" -Recurse -Exclude "Do_not_copy\SI\Tools" -Force
Get-ChildItem $DestinationFolder\ -Include "Tools", "Support", "Installer", "GUI", "Filer", "Documentation" -Recurse -Force | ForEach-Object { Remove-Item -Path $_.FullName -Recurse -Force }
The -Recurse switch does not work properly on Remove-Item (it will try to delete folders before all the subfolders in the folder have been deleted).
Sorting the fullnames in descending order by length ensures than no folder is deleted before all the child items in the folder have been deleted.
Try
$rootFolder = '\\google\server\ToDelete'
# create a regex of the folders to exclude
$Exclude = 'Do_not_copy\SI\Tools' # this can be an array of items to exclude or a single item
# each item will be Regex Escaped and joined together with the OR symbol '|'
$notThese = ($Exclude | ForEach-Object { [Regex]::Escape($_) }) -join '|'
# the array of folder names (not full paths) to delete
$justThese = 'Tools', 'Support', 'Installer', 'GUI', 'Filer', 'Documentation'
(Get-ChildItem -Path $rootFolder -Directory -Recurse -Include $justThese).FullName |
Where-Object { $_ -notmatch $notThese } |
Sort-Object Length -Descending |
Remove-Item -Recurse -Confirm:$false -Force -WhatIf
As usual, I have added the -WhatIf safety switch, so no folders will be deleted and in the console you can see what would happen.
When satisfied the correct folders will be removed, comment out or remove that -WhatIf switch and run again
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
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.
#>
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 }
I have a PowerShell script that recursively deletes all files and folders, but excludes certain folders as they should not be deleted.
It works 100%, but my problem is performance. I need this to run a lot faster.
Any ideas on how to make this faster?
Write-Host "Purging $InstallationDirectorySite - Deleting files..."
$FolderExlusions = (
"App_Data",
"Logs",
"TEMP",
"ExamineIndexes",
"DistCache",
"GitPathProviderRepository"
)
[regex] $files_regex = "Logs|ExamineIndexes|DistCache*|GitPathProviderRepository*"
if(Test-Path $InstallationDirectorySite) {
Get-ChildItem -Path $InstallationDirectorySite -Recurse -Exclude $FolderExlusions |
Where-Object {$_.FullName -notmatch $files_regex} |
Remove-Item -Recurse
}
else {
Write-Output "$InstallationDirectorySite doesn't exist"
}
You are in fact filtering the excluded folders twice.
The first time using the -Exclude parameter and the second time using a Regex -match.
However, the Exclude parameter takes a string array, not a single string with keywords separated by a comma and a newline as you get from the 'here-string'.
See Get-ChildItem
Also, the regex you use is wrong, because the asteriks * in regex is a quantifier, not a wildcard character.
I suggest you filter once using either the -Exclude parameter like this (here the asteriks is a Wildcard):
$FolderExlusions = "App_Data","Logs","TEMP","ExamineIndexes","DistCache*","GitPathProviderRepository*"
Get-ChildItem -Path $InstallationDirectorySite -Recurse -Exclude $FolderExlusions | Remove-Item -Recurse -WhatIf
Or use only the regex method in a Where-Object clause like this:
$FolderExlusions = "^(App_Data|Logs|TEMP|ExamineIndexes|DistCache.*|GitPathProviderRepository.*)"
Get-ChildItem -Path $InstallationDirectorySite -Recurse | Where-Object { $_.Name -notmatch $FolderExlusions } | Remove-Item -Recurse -WhatIf
Remove the -WhatIf if you are satisfied with the results.
Hope that helps
Im still generally new to powershell, and I am trying to create a program that will take files based on their name, and move them into folders that have a similar name but not exactly the same.
For example, Lets say I have 3 files, Apples.txt, Grapes.txt, and Oranges.txt. And I want to move them into corresponding folders, ApplesUSA, GrapesNY, OrangesFL.
I could just hard code it using a loop and a If-Then Statement. i.e If Apples.txt exists move to ApplesUSA. But I want it to be dynamic, so if other files and folders are added later I dont have to update the code. Is there a way to write a statement that would say if FileA and FolderB are similar in name (both contain apples in the name somewhere) then move fileA to FolderB and so on.
Any help appreciated. Thanks!!!!
try Something like this
$PathWithFile="C:\temp\Test"
$PathWithDir="C:\temp\Test"
Get-ChildItem $PathWithFile -file -Filter "*.txt" | %{
$CurrentFile=$_
$Dirfounded=Get-ChildItem $PathWithDir -Directory | where {$_ -match $CurrentFile.BaseName} | select FullName -First 1
if ($Dirfounded -ne $null)
{
move-Item $CurrentFile.FullName -Destination $Dirfounded.FullName -WhatIf
}
}
A oneliner similar to #Esperento's
gci *.txt -af|%{$File=$_.FullName;gci "$($_.BaseName)*" -ad|%{Move $File -Dest $($_.FullName) -whatif}}
The verbose version:
PushD "X:\path\to\base\folder"
Get-ChildItem *.txt -File | ForEach-Object{
$File = $_.FullName
Get-ChildItem "$($_.BaseName)*" -Directory | ForEach-Object {
Move-Item $File -Destination $_.FullName -whatif
}
}
PopD
Both versions require PowerShell V3 for the -File and -Directory parameters (and their aliases -af/-ad) This can be substituted by an additional |Where-Object{ $_.PSIsContainer} respective | Where-Object{!$_.PSIsContainer}
I am attempting to delete all directories, sub-directories, and the files contained in them based on a filter that specifies the required directory/sub-directory name.
For example, if I have c:\Test\A\B.doc, c:\Test\B\A\C.doc, and c:\Test\B\A.doc and my filter specifies all directories named 'A', I would expect the remaining folders and files to be c:\Test, c:\Test\B and c:\Test\B\A.doc respectively.
I am trying to do this in PowerShell and am not familiar with it.
The following 2 examples will delete all of the files that match my specified filter, but the files that match the filter as well.
$source = "C:\Powershell_Test" #location of directory to search
$strings = #("A")
cd ($source);
Get-ChildItem -Include ($strings) -Recurse -Force | Remove-Item -Force –Recurse
and
Remove-Item -Path C:\Powershell_Test -Filter A
I would use something like this:
$source = 'C:\root\folder'
$names = #('A')
Get-ChildItem $source -Recurse -Force |
Where-Object { $_.PSIsContainer -and $names -contains $_.Name } |
Sort-Object FullName -Descending |
Remove-Item -Recurse -Force
The Where-Object clause restricts the output from Get-ChildItem to just folders whose names are present in the array $names. Sorting the remaining items by their full name in descending order ensures that child folders get deleted before their parent. That way you avoid errors from attempting to delete a folder that had already been deleted by a prior recursive delete operation.
If you have PowerShell v3 or newer you can do all filtering directly with Get-ChildItem:
Get-ChildItem $source -Directory -Include $names -Recurse -Force |
Sort-Object FullName -Descending |
Remove-Item -Recurse -Force
I don't think you can do it quite that simply. This gets the list of directories, and breaks the path into its constituent parts, and verifies whether the filter matches one of those parts. If so, it removes the whole path.
It adds a little caution to handle if it already deleted a directory because of nesting (the test-path) and the -Confirm helps ensure that if there's a bug here you have a chance to verify the behavior.
$source = "C:\Powershell_Test" #location of directory to search
$filter = "A"
Get-Childitem -Directory -Recurse $source |
Where-Object { $_.FullName.Split([IO.Path]::DirectorySeparatorChar).Contains($filter) } |
ForEach-Object { $_.FullName; if (Test-Path $_) { Remove-Item $_ -Recurse -Force -Confirm } }