Solved

windows powershell delete files older than with exclusions

Posted on 2013-06-09
22
1,466 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 68

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 18

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 68

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
 

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 18

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 68

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 68

Assisted Solution

by:Qlemo
Qlemo earned 100 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 39

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 18

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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 68

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 68

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 39

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 39

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 39

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 68

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 39

Accepted Solution

by:
footech earned 400 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

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Are you one of those front-line IT Service Desk staff fielding calls, replying to emails, all-the-while working to resolve end-user technological nightmares? I am! That's why I have put together this brief overview of tools and techniques I use in o…
A procedure for exporting installed hotfix details of remote computers using powershell
Learn the basics of strings in Python: declaration, operations, indices, and slicing. Strings are declared with quotations; for example: s = "string": Strings are immutable.: Strings may be concatenated or multiplied using the addition and multiplic…
Learn the basics of if, else, and elif statements in Python 2.7. Use "if" statements to test a specified condition.: The structure of an if statement is as follows: (CODE) Use "else" statements to allow the execution of an alternative, if the …

746 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

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now