Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

windows powershell delete files older than with exclusions

Posted on 2013-06-09
22
Medium Priority
?
1,954 Views
Last Modified: 2013-06-16
Hi all,

I'd like to delete files and sub-folders older than 14 days while excluding specific folders.

main path:  C:\Projects

under c:\Projects there are subfolders with project names, 1 folder for each project.

I'd like to KEEP the 1st level of all folders, meaning, keeping all projects folders while deleting only their content (files+folders) older than 14 days (all depth-levels), lastly, I'd like to be able to specify in the script certain paths\folders which will remain intact, including their contents.

Any chance someone here is able to write that script?

greatly appreciated!

thanks.
0
Comment
Question by:iNc0g
  • 7
  • 7
  • 5
  • +1
22 Comments
 
LVL 71

Expert Comment

by:Qlemo
ID: 39233201
The objective is not completely clear. Do you want to delete the folders if ONLY older files are in them, or delete all files which are older? That makes a big diffence - the former deletes old projects, the latter old files.
0
 
LVL 19

Expert Comment

by:Raheman M. Abdul
ID: 39233267
Try:

Replace pr3 with level 1 project folders if you want them not to delete
Replace new with level 2 or above project folders if you want them not to delete


Get-ChildItem C:\temp\projects -Exclude pr3 -Recurse | foreach { Get-ChildItem $_ -Recurse -Exclude new | Where {$_.creationtime -lt (Get-Date).AddDays(-14)} |Remove-Item -Recurse }
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 39233291
The code above will go thru the files and folders twice, and not allow to supply any level of folder to exclude.
cls
$ErrorActionPreference = 'Stop'
$root = 'C:\Projects'
$ExcludePaths = '\Project1', '\Project2\DoNotRemove'
$date = (Get-Date).AddDays(-14)
 
 get-childitem $root | where { $_.PsIsContainer } | get-childitem -recurse | % {
   $found = $false
   foreach ($path in $ExcludePaths) {if ($found = $found -or ($_.Path -like ($path+'\*'))) {break}}
   if (!$found) {$_}
 } | where { $_.LastWriteTime -lt $date } | Remove-Item -Recurse -WhatIf

Open in new window

This will simulate the deletion. Remove the -WhatIf to really perform the deletion.
0
Lessons on Wi-Fi & Recommendations on KRACK

Simplicity and security can be a difficult  balance for any business to tackle. Join us on December 6th for a look at your company's biggest security gap. We will also address the most recent attack, "KRACK" and provide recommendations on how to secure your Wi-Fi network today!

 

Author Comment

by:iNc0g
ID: 39233882
Hi Guys! thank you very much for your responses.

Your scripts look great, the final touch (if possible) would be to make all project folders intact:
-Projects
     -Project1
     -Project2
     -Project3
     -Project4

Goals:
1) I'd like to keep ALL 1st level folders (project1,2,3,4.....etc) no matter what.

2) Delete only the contents of the project folders (files+subfolders in all depths) ONLY if the content is older than 14 days, in no situation should the 1st level folders be deleted, just their contents based on older than 14 days.

3) The ability to exclude certain projects/paths inside projects

any chance this is possible ?  ^.^

Many thanks again!
0
 
LVL 19

Expert Comment

by:Raheman M. Abdul
ID: 39233907
Make a copy of your project folders and run my script on them
I have tried and it worked for me.
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 39234022
Obviously I was ignoring the "keep all 1st level folders" stuff ... But that is all what was missing.
cls
$ErrorActionPreference = 'Stop'
$root = 'C:\Projects'
$ExcludePaths = '\Project1', '\Project2\DoNotRemove'
$date = (Get-Date).AddDays(-14)
 
 get-childitem $root | where { $_.PsIsContainer } |
  get-childitem | where { $_.PsIsContainer } |
  get-childitem -recurse | % {
   $found = $false
   foreach ($path in $ExcludePaths) {if ($found = $found -or ($_.Path -like ($path+'\*'))) {break}}
   if (!$found) {$_}
 } | where { $_.LastWriteTime -lt $date } | Remove-Item -Recurse -WhatIf

Open in new window

0
 

Author Comment

by:iNc0g
ID: 39234515
I ran the scripts on some test folders, here are some results:

@Qlemo
********
I used folder C:\drivers\win with 4 subfolders in it: AUDIO, DISPLAY6, INTELINF, MONITOR
I specified 2 paths to exclude from deletion:
$ExcludePaths = '\AUDIO', '\INTELINF\infinst_autol'
while root folder is: $root = 'C:\drivers\win'

ran the script with -whatif and it excluded AUDIO folder but performed the "remove file" on c:\drivers\win\INTELINF\infinst_autol" and "remove-directory" of all of infinst_autol sub-folders while it was supposed to exclude it from deletion.

the 4th folder after INTELINF called "MONITOR" got skipped from deletion for some reason.

@marahman3001
***************
ran the script with -whatif:
Get-ChildItem C:\drivers\win -Exclude Audio -Recurse | foreach { Get-ChildItem $_ -Recurse -Exclude INTELINF\infinst_autol | Where {$_.creationtime -lt (Get-Date).AddDays(-14)} |Remove-Item -Recurse -WhatIf }

Open in new window


it managed to exclude AUDIO folder but performed remove-file and remove-directory on
"INTELINF\infinst_autol" which was supposed to be excluded.

it then continued to the 4th folder "MONITOR" (as should) and performed the remove file operation on it since it doesn't contain any subfolders.
0
 
LVL 71

Assisted Solution

by:Qlemo
Qlemo earned 400 total points
ID: 39235490
Sorry for that rubbish :/.
cls
$ErrorActionPreference = 'Stop'
$root = 'C:\Drivers\win'
$ExcludePaths = '\AUDIO', '\INTELINF\infinst_autol'
$date = (Get-Date).AddDays(-14)
 
get-childitem $root | where { $_.PsIsContainer } |
  get-childitem | where { $_.PsIsContainer } |
  get-childitem -recurse | % {
   $found = $false
   foreach ($path in $ExcludePaths) {if ($found = $found -or ($_.FullName -like "$root$path\*")) {break}}
   if (!$found) {$_}
 } | where { $_.LastWriteTime -lt $date } | Remove-Item -Recurse -WhatIf

Open in new window

0
 
LVL 41

Expert Comment

by:footech
ID: 39235498
Here you go.  I borrowed some of the ideas from Qlemo.
$ErrorActionPreference = 'Stop'
$root = 'C:\drivers\win'
$ExcludePaths = '\AUDIO', '\INTELINF\infinst_autol'
$date = (Get-Date).AddDays(-14)

Get-ChildItem $root |
 Where { $_.PsIsContainer } |
 Get-ChildItem -Recurse | ForEach {
   $found = $false
   foreach ($path in $ExcludePaths)
   {
     if (($_.FullName | Split-Path -Parent) -like (Join-Path $root $path))
     {$found = $true}
   }
   if (!$found)
   {$_}
 } | Where { $_.LastWriteTime -lt $date } | Remove-Item -Recurse -WhatIf

Open in new window

0
 
LVL 19

Expert Comment

by:Raheman M. Abdul
ID: 39235505
Try just infinst_autol instead of INTELINF\infinst_autol
0
 

Author Comment

by:iNc0g
ID: 39240196
@Qlemo - We're making some progress :-)  ran your script again, results are:
Exclusions - working OK

Deletions - working ok EXCEPT for folder "MONITOR" which is the last folder, for some reason it does not "touch" that folder or mention it on the script output while in reality it should delete all content of MONITOR folder and leave the folder empty.

@footech - Ran your script, results are:
Exclusions - only the 1st mentioned exclusion is actually excluded ('\AUDIO')
the 2nd exclusion '\INTELINF' or '\INTELINF\infinst_autol' doesn't get excluded.

Deletions - Working OK, including the last folder "MONITOR" as should.

@marahman3001 - Did what you suggested, no go, still getting deleted.


Thank you all very much for your hard efforts!! waiting to hear from you ^.^
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 39240245
Had to remove one level of get-childitem - the MONITOR folder does not have any subfolders, I presume, and my code only looked for subfolders.
cls
$ErrorActionPreference = 'Stop'
$root = 'C:\Drivers\win'
$ExcludePaths = '\AUDIO', '\INTELINF\infinst_autol'
$date = (Get-Date).AddDays(-14)
 
get-childitem $root | where { $_.PsIsContainer } |
  get-childitem -recurse | % {
   $found = $false
   foreach ($path in $ExcludePaths) {if ($found = $found -or ($_.FullName -like "$root$path\*")) {break}}
   if (!$found) {$_}
 } | where { $_.LastWriteTime -lt $date } | Remove-Item -Recurse -WhatIf
 

Open in new window

0
 

Author Comment

by:iNc0g
ID: 39240257
@Qlemo - Some folders have subfolders of several depth levels and some doesn't have at all.  should your script do the job in that case ?
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 39240283
The new one - yes. The old one did the folder, subfolders and files of
   c:\drivers\win\monitor\one        
   c:\drivers\win\monitor\one\two\three\four
but NO files in
   c:\drivers\win\monitor
Your requirement is to leave the folder MONITOR itself, but remove anything it contains if old.
0
 

Author Comment

by:iNc0g
ID: 39240340
@Qlemo - 2 problems now:
1. $ExcludePaths only excludes the 1st mentioned exclusion and ignores the 2nd .
2. $ExcludePaths is unable to exclude 2nd level subfolders such as: \AUDIO\Folder , only manages to exclude the root folder \AUDIO .
0
 
LVL 41

Expert Comment

by:footech
ID: 39242097
Think I've got it all worked out.
$root = 'C:\Drivers\win'
$ExcludePaths = '\AUDIO', '\INTELINF\infinst_autol'
$date = (Get-Date).AddDays(-14)

Get-ChildItem $root |
 Where { $_.PsIsContainer } |
 Get-ChildItem -Recurse | % {
   $exclude = $false
   foreach ($path in $ExcludePaths)
   {
     If (!$_.PsIsContainer)
     {
       If ("$($_.FullName | Split-Path -Parent)\" -like "$(Join-Path $root $path)\*")
       {$exclude = $true}
     }
     ElseIf ($_.PsIsContainer)
     {
       If ("$($_.FullName)\" -like "$(Join-Path $root $path)\*")
       {$exclude = $true}
     }
   }
   if (!$exclude)
   {$_}
 } | where { $_.LastWriteTime -lt $date } | Remove-Item -Recurse -WhatIf

Open in new window

0
 

Author Comment

by:iNc0g
ID: 39243518
footech - Ran your script, it ignores files age which resides in 3rd level and above folder depth.

c:\drivers\win\INTELINF\infinst_autol\ia64\ENU\FileNotOlderThan14Days.txt
the above path got deleted even though it contains the .txt file which is not older than 14 days (modified it intentionally).
0
 
LVL 41

Expert Comment

by:footech
ID: 39243992
Yikes!  I see what is happening.  A parent folder (which doesn't meet any of the exclude criteria) is being deleted (along with all children) before the children are examined.  Try this out.  It's not real polished and probably contains some cruft that could be removed, but just checking functionality now.
$root = 'C:\Drivers\win'
$ExcludePaths = '\AUDIO', '\INTELINF\infinst_autol'
$date = (Get-Date).AddDays(-14)
$out = @()
Get-ChildItem $root |
 Where { $_.PsIsContainer } |
 Get-ChildItem -Recurse | ForEach {
   $exclude = $false
   foreach ($path in $ExcludePaths)
   {
     If (!$_.PsIsContainer)
     {
       If ("$($_.FullName | Split-Path -Parent)\" -like "$(Join-Path $root $path)\*")
       {$exclude = $true}
     }
     ElseIf ($_.PsIsContainer)
     {
       If ("$($_.FullName)\" -like "$(Join-Path $root $path)\*")
       {$exclude = $true}
     }
   }
   if (!$exclude)
   {$out += $_}
 }
$folderstodelete = $out | ? {$_.psiscontainer -and $_.lastwritetime -lt $date} | Select -ExpandProperty fullname | sort length -Descending
$filesnottodelete = $out | ? {!$_.psiscontainer -and $_.lastwritetime -ge $date} | Select -ExpandProperty fullname | sort length -Descending
$filestodelete = $out | ? {!$_.psiscontainer -and $_.lastwritetime -lt $date} | Select -ExpandProperty fullname | sort length -Descending
$exdir = @()
foreach ($str in $filesnottodelete)
{
    # get list of parent paths
    while ($str -match "\\")
    {
        $str = $str | split-path
        $exdir += $str
    }
}
$exdir = $exdir | Select -Unique
$folderstodelete | sort -Descending | ? {$exdir -notcontains $_} | Remove-Item -Recurse -WhatIf
$filestodelete | Remove-Item -WhatIf

Open in new window

0
 

Author Comment

by:iNc0g
ID: 39244296
And we have a winner!
@footech - your script worked.
regarding the cruft - it seems like it tried deleting the data a 2nd time therefor received:

Remove-Item : Cannot find path 'C:\Temp\project1\ossv\manageability\InstallHostAgentPlugins.exe' because it does not exist.
At C:\scripts\ftp_delete.ps1:40 char:29
+ $filestodelete | Remove-Item <<<<
    + CategoryInfo          : ObjectNotFound: (C:\Temp\project...gentPlugins.exe:String) [Remove-Item], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand

Open in new window


several times.

any chance to clean up the cruft without impairing the script?  it finally works I am afraid to get in a loop again ;-)
0
 
LVL 41

Expert Comment

by:footech
ID: 39245405
The simplest would be to just swap lines 39 and 40.  I'll try to look at cleaning it up a bit more later.
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 39245629
Another simple way is to set the error action of remove-item to continue without throwing an error:
$folderstodelete | sort -Descending | ? {$exdir -notcontains $_} | Remove-Item -Recurse -WhatIf -EA SilentlyContinue
$filestodelete | Remove-Item -WhatIf -EA SilentlyContinue

Open in new window

That is the "brute force" approach ;-).
0
 
LVL 41

Accepted Solution

by:
footech earned 1600 total points
ID: 39246748
I don't know if it's more efficient to just ignore the error condition with "-ea SilentlyContinue" or to order the deletions in a "leaf to root" manner so that the error is never encountered.  Kind of an interesting question...  If you equate the process to cutting down or pruning a tree, with one method you start at the top of the tree and you:
 - remove any unwanted leaves
 - once all the leaves are removed from a small branch, remove the branch
 - repeat the process, working your way down the branch (towards the trunk), and each time you encounter a spot where a new branch forms you have to decide whether to cut it off
 - eventually you would be left with a million leaves and branch pieces (each one technically a branch) on the ground.
The second method would be more of a bottom to top process, working from the root to the leaves:
 - as you work your way up (and out), at each branch you would decide whether to cut the branch off
- once a branch was removed you would no longer have to worry about any of its sub-branches or leaves (they're already gone)
Method 1 would be extremely inefficient for humans to do.  Imagine how many cuts it would take to remove an entire tree?
Method 2 would be much more efficient.  Just cut it off at the trunk and you're done.
But when we're talking about computers and filesystems, is there really any difference?  Does deleting a folder (with all it's files and subfolders) differ from deleting all the files, then the subfolders, and then the parent folder?
I'm not well-versed enough with filesystems to know the answer.

Anyway, I went with the ordered approach below.
$root = 'C:\Drivers\win'
$ExcludePaths = '\AUDIO', '\INTELINF\infinst_autol'
$date = (Get-Date).AddDays(-14)
$out = @()
Get-ChildItem $root |
 Where { $_.PsIsContainer } |
 Get-ChildItem -Recurse | ForEach {
   $exclude = $false
   foreach ($path in $ExcludePaths)
   {
     If (!$_.PsIsContainer)
     {
       If ("$($_.FullName | Split-Path -Parent)\" -like "$(Join-Path $root $path)\*")
       {$exclude = $true}
     }
     ElseIf ($_.PsIsContainer)
     {
       If ("$($_.FullName)\" -like "$(Join-Path $root $path)\*")
       {$exclude = $true}
     }
   }
   If (!$exclude)
   {$out += $_}
 }
$filesnottodelete = $out | Where {!$_.PsIsContainer -and $_.LastWriteTime -ge $date} | Select -ExpandProperty Fullname | Sort length -Descending
$exdir = @()
foreach ($str in $filesnottodelete)
{
    # get list of parent paths
    while ($str -match "\\")
    {
        $str = $str | Split-Path
        $exdir += $str
    }
}
$exdir = $exdir | Select -Unique
$out |
 Where {$_.lastwritetime -lt $date -and $exdir -notcontains $_.Fullname} |
 Select -ExpandProperty Fullname |
 Sort length -Descending |
 Remove-Item

Open in new window

0

Featured Post

Lessons on Wi-Fi & Recommendations on KRACK

Simplicity and security can be a difficult  balance for any business to tackle. Join us on December 6th for a look at your company's biggest security gap. We will also address the most recent attack, "KRACK" and provide recommendations on how to secure your Wi-Fi network today!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Windows 10 came with  a lot of built in applications, Some organisations leave them there, some will control them using GPO's. This Article is useful for those who do not want to have any applications in their image (example:me).
The Windows functions GetTickCount and timeGetTime retrieve the number of milliseconds since the system was started. However, the value is stored in a DWORD, which means that it wraps around to zero every 49.7 days. This article shows how to solve t…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
In this fourth video of the Xpdf series, we discuss and demonstrate the PDFinfo utility, which retrieves the contents of a PDF's Info Dictionary, as well as some other information, including the page count. We show how to isolate the page count in a…
Suggested Courses

885 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question