Delete X number of files X Old days -Powershell - powershell

Get-ChildItem –Path “H:\backups” –Recurse | Where-Object{$_.CreationTime –lt (Get-Date).AddDays(-4)} | Remove-Item
I have this Script that can delete files older than 4 days. However I want to control that it should delete X number of files+folder 4 days old and also want to get the logs so that I see it later.
Below is the script, Is it fine?
$Now = Get-Date
$Days = "4"
$TargetFolder = "H:\backups"
$LastWrite = $Now.AddDays(-$days)
$Files = get-childitem $TargetFolder -include *.* -recurse -force
Where {$_.CreationTime -le "$LastWrite"}
foreach ($i in Get-ChildItem $TargetFolder -recurse)
{
if ($i.CreationTime -lt ($(Get-Date).AddDays(-10)))
{
Remove-Item $Files -recurse -force
}
}
Write-Output $Files >> c:\delete.log

I believe this would work, allowing you to pass a path and a number of days you'd want to keep as parameters:
function RemoveOld($path, $filefilter, $daystokeep)
{
$findfiles = #(Get-ChildItem -Path $path -Include $filefilter)
$toRemove = $findfiles | Where-Object CreationTime -le (Get-Date).AddDays(-$daystokeep)
$toRemove >> c:\temp\delete.log
$toRemove | Remove-Item
}
RemoveOld -path "H:\backups\*" -filefilter "*" -daystokeep 4
May I also suggest, rather than deleting files older than a given time-frame, remove the files older than the most recent X number of files. In this way you'd always keep some number of files and not end up in a situation where all your files are deleted if the backup somehow fails to run for several days. It could be an option depending on how your folder structure is set up (modify the filefilter parameter so you don't delete more than desired).
function RemoveOlder($path, $filefilter, $filestokeep)
{
$findfiles = #(Get-ChildItem -Path $path -Include $filefilter)
if ($findfiles.Count -gt $filestokeep) {
$findfiles | Sort-Object LastWriteTime -Descending | Select-Object -Last ($findfiles.Count - $filestokeep) | Remove-Item
}
}
RemoveOlder -path "H:\backups\*" -filefilter "*" -filestokeep 4

On your original command, add this:
-Verbose 4>&1 | Out-File -FilePath D:\delete.log -Append
4 is Verbose output so this is redirecting Verbose to stdout 1, then redirecting both to a log file. The final command would be:
Get-ChildItem –Path “H:\backups” –Recurse `
| Where-Object{$_.CreationTime –lt (Get-Date).AddDays(-4)} `
| Remove-Item -Verbose 4>&1 | Out-File -FilePath C:\delete.log -Append
Update: If you want the command to run without interaction and continue on errors etc. for a scheduled task, add additional parameters to Remove-Item:
Get-ChildItem –Path “H:\backups” –Recurse `
| Where-Object{$_.CreationTime –lt (Get-Date).AddDays(-4)} `
| Remove-Item -Verbose -Force -ErrorAction SilentlyContinue -Confirm:$false 4>&1 `
| Out-File -FilePath C:\delete.log -Append

Related

I'm writing my first powershell script to remove old TMP/LOG files on exchange

I'm writing a custom script to keep our Exchange servers clean. It consists of several parts.
The last part is to clean TEMP folders, and it's working with no problems.
The first part is where my problem is. I want to select all .BAK .TMP and .XML files and delete them if they are over 3 days old, and select and delete all .log files if they are over 30 days old. But no files are being selected.
$Path ="$env:SystemDrive\Program Files (x86)\GFI\MailEssentials\EmailSecurity\DebugLogs\", "$env:SystemDrive\Program Files (x86)\GFI\MailEssentials\AntiSpam\DebugLogs\", "$env:SystemDrive\inetpub\logs", "$env:windir\System32\LogFiles"
# How long do you want to keep files by default?
$Daysback = "3"
# How long do you want to keep .log files? (Recommended 30 days at least)
$DaysbackLog = "30"
$DatetoDelete = (Get-Date).AddDays(-$Daysback)
$DatetoDeleteLog = (Get-Date).AddDays(-$DaysbackLog)
Get-ChildItem $Path -Recurse -Hidden | Where-Object {($_.extension -like ".log" -and $_.LastWriteTime -lt $DatetoDeleteLog)} | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
Get-ChildItem $Path -Recurse -Hidden | Where-Object {($_.extension -like ".bak", "tmp", "xml" -and $_.LastWriteTime -lt $DatetoDelete)} | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
# The following lines clears temp folder and empty folders in the temp folder.
Get-ChildItem "$env:windir\Temp", "$env:TEMP" -recurse | Where-Object { $_.LastWriteTime -lt $DatetoDelete } | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
Get-ChildItem "$env:windir\Temp", "$env:TEMP" -recurse | Where-Object { $_.LastWriteTime -lt $DatetoDelete } | Where {$_.PSIsContainer -and #(Get-ChildItem -LiteralPath:$_.fullname).Count -eq 0} | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
There are a few ways to do this, but much of it is based on personal preference and/or performance. The latter of which is not likely to be a big design factor here.
$Path = #(
"$env:SystemDrive\Program Files (x86)\GFI\MailEssentials\EmailSecurity\DebugLogs\"
"$env:SystemDrive\Program Files (x86)\GFI\MailEssentials\AntiSpam\DebugLogs\"
"$env:SystemDrive\inetpub\logs"
"$env:windir\System32\LogFiles"
)
# Extensions
$Extensions = "*.bak", "*.tmp", "*.xml"
# Temp folders to clean up
$Temps = "$env:windir\Temp", "$env:TEMP"
# How long do you want to keep files by default?
$Daysback = "3"
# How long do you want to keep .log files? (Recommended 30 days at least)
$DaysbackLog = "30"
$DatetoDelete = (Get-Date).AddDays(-$Daysback)
$DatetoDeleteLog = (Get-Date).AddDays(-$DaysbackLog)
Get-ChildItem $Path -Filter "*.log" -Recurse -Hidden |
Where-Object { $_.LastWriteTime -le $DatetoDeleteLog } |
Remove-Item -Force -ErrorAction SilentlyContinue -WhatIf
# > Move filtering left, which works because you are only looking for a single
# extension.
# > Change to -le to accommodate edge case where $_.LastWriteTime is right on
# the boundary.
$Extensions |
ForEach-Object{
Get-ChildItem $Path -Filter $_ -Recurse -Hidden
} |
Where-Object { $_.LastWriteTime -le $DatetoDelete } |
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
# Set up extensions as an array of wild card filters.
# -Filter is much faster than -Include which may be another alternative approach
Get-ChildItem $Temps -File -Recurse |
Where-Object { $_.LastWriteTime -le $DatetoDelete } |
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
Get-ChildItem $Temps -Directory -Recurse |
Where-Object { !$_.GetFileSystemInfos() } |
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
I haven't tested any of the refactor. However, the approach is to simply rerun the Get-ChildItem cmdlet for each needed scenario. In my experience that's faster than trying to use the -Include parameter to grab all the extensions in 1 shot, while still be faster and easier to read than adding to a Where{} clause to filter on extension.
In the part for clearing the temp folders. I use the .Net Method .GetFileSystemInfos() on the [System.IO.DirectoryInfo] objects returned from Get-ChildItem. The method returns an array of all child objects, so if it's null we know the folder is empty. That sounds complicated, but as you can see it significantly shrinks the code and will likely perform better. I use the -File & -Directory parameters respectively to make sure to make sure I've got the right object types.
This is a little more advanced, but another way I played with to clean up the temp folders is to use a ForEach-Object loop with 2 process blocks.
$Temps |
ForEach-Object -Process {
# 1st process block get Empty directories:
Get-ChildItem -Directory -Recurse |
Where-Object{ !$_.GetFileSystemInfos() }
}, {
# 2nd process block get files older than the boundary date.
Get-ChildItem -File -Recurse |
Where-Object { $_.LastWriteTime -le $DatetoDelete }
} |
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -WhatIf
Again untested, and I'm not sure how this will preform. Nevertheless, since I developed it thought I'd share.
Note: the -Process argument is necessary so that ForEach-Object assigns both block to process.
Check out ForEach-Object with Multiple Script Blocks for more information.

Delete old files after X days by default with exception on certain dirs [Powershell]

I'm trying to fix the following scenario:
I have directory which has multiple subdirectories and files where I need to set something like retention policy, by default I would set to have files no older than 365 days where there are some special directories where I would like to keep for a different period of time other than my default value. These directories are specified in a txt file with the following syntax
Content of Drive:\Path\to\special_dirs.txt
D:\Path\to\vendor1 -396
D:\Path\to\vendor2 -45
This is what I have come up so far (It is working on the special directories only, the next part where I want to proceed with the rest does not work):
# Script to remove old files from Archive: D:\Path\to
# Declaring variables to be used
$controlfile = "Drive:\Path\to\list\special_dirs.txt"
$dir = "D:\Path\to"
$default_days = "-365"
$excluded_dirs = Get-Content $controlfile | Foreach-Object {$_.Split()[0]}
foreach ($line in Get-Content $controlfile) {
$split = $line.split(" ")
$file_path = $split[0]
$max_days = $split[1]
Get-ChildItem $file_path -Include *.* -File -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays($max_days))} | Remove-Item -Force -Recurse -Verbose -WhatIf
}
# Removing everything else older than 365 days old
Get-ChildItem -Path $dir -Include *.* -File -Recurse -Directory -Exclude $excluded_dirs | Where-Object {($_.LastWriteTime -lt $curr_date.AddDays($default_days))} | Remove-Item -Force -Recurse -Verbose -WhatIf
I'm not looking to remove directories even if they are empty, I just want to remove files, the last part of the script just deletes everything older than 365 day where there is a directory that I would like to keep 30 days more than the default period of time, any ideas on how to get this done?
I used the file list so that I can keep adding directories to vendors where I can keep longer than or even less than the default days.
What about something like this?..
$30DayDirs = #('C:\Temp\Test1\','C:\Temp\Test2\')
$60DayDirs = #('C:\Temp\Test3\')
$365DayDirs = #('C:\Temp\Test4\')
foreach($file in $30DayDirs){
$file = Get-ChildItem $30DayDirs -Recurse |where LastWriteTime -LT (Get-Date).AddDays(-30)
Remove-Item $file.FullName -Force -Recurse
}
foreach($file in $60DayDirs){
$file = Get-ChildItem $60DayDirs -Recurse |where LastWriteTime -LT (Get-Date).AddDays(-60)
Remove-Item $file.FullName -Force -Recurse
}
foreach($file in $365DayDirs){
$file = Get-ChildItem $365DayDirs -Recurse |where LastWriteTime -LT (Get-Date).AddDays(-365)
Remove-Item $file.FullName -Force -Recurse
}
I have fixed my issue with the following:
# Script to remove old files from DFS Archive: D:\Path\to
# Declaring variables to be used
$controlfile = "Drive:\Path\to\list\special_dirs.txt"
$dir = "D:\Path\to"
$default_days = "-365"
$excluded_dirs = Get-Content $controlfile | Foreach-Object {$_.Split()[0]}
# Removing old files from special directories first
cd $dir
foreach ($line in Get-Content $controlfile) {
$split = $line.split(" ")
$file_path = $split[0]
$max_days = $split[1]
Get-ChildItem $file_path -Include *.* -File -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays($max_days))} | Remove-Item -Force -Recurse -Verbose
}
# Removing everything else older than 365 days old
Get-ChildItem $dir -Directory -Exclude $excluded_dirs | Get-ChildItem -Include *.* -File -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays($default_days))} | Remove-Item -Force -Recurse -Verbose
And I have modified my special_dirs.txt file to:
vendor1 -396
vendor2 -45

How to search inside three paths and copy the name on a file.list

I am wondering if there is better way to make a script on PowerShell these instructions:
Search on 3 paths. Ex.
$LOGDIRS="C:\NETiKA\GED\Production\RI\log";"C:\NETiKA\GED\Test\RI\log";"C:\NETiKA\Tomcat-8.0.28\logs"
Find all files that are older than 7 days and copy on a file that I will call file.list . EX. > C:\Test\file.list
When I copied on my file.list, I need to search all the name of the files and delete them.
Apparently when you have more than thousands of file, this is the
fastest way to delete.
$LOGDIRS=C:/NETiKA/GED/Production/RI/log;C:/NETiKA/GED/Test/RI/log;C:/NETiKA/Tomcat-8.0.28/logs
$KEEP=-7
Get-ChildItem -Path $LOGDIRS -Recurse -Directory -Force -ErrorAction SilentlyContinue |
Select-Object FullName > files.list |
Foreach-Object {
if ($_.LastAccessTime -le (get-date).adddays($KEEP)) {
remove-item -recurse -force $_
}
};
Something like this should help you get started.
$path1 = "E:\Code\powershell\myPS\2018\Jun"
$path2 = "E:\Code\powershell\myPS\2018\Jun\compareTextFiles"
$path3 = "E:\Code\powershell\myPS\2018\May"
$allFiles = dir $path1, $path2, $path3 -File
$fileList = New-Item -type file file.list -Force
$keep = -7
$allFiles | foreach {
if ($_.LastAccessTime -le (Get-Date).AddDays($keep)) {
"$($_.FullName) is older than 7 days"
$_.FullName.ToString() | Out-File $fileList -Append
}
else {
"$($_.FullName) is new"
}
}
You can add deletion in the code in IF Block if you wish or check the file and do it later on. Your code has many issues which are very basic to PowerShell, e.g: once you use Select-Object the next pipeline will only receive the property you selected. You have tried using LastAccessTime in later pipe when you only selected to go ahead with FullName property.
Also, redirecting to a file and again using pipeline looks very messy.
Remove-Item accepts piped input and a
Where will filter the age
to first check what would be deleted I appended a -WhatIf to the Remove-Item
$LOGDIRS="C:\NETiKA\GED\Production\RI\log","C:\NETiKA\GED\Test\RI\log","C:\NETiKA\Tomcat-8.0.28\logs"
$KEEP=-7
Get-ChildItem -Path $LOGDIRS -Recurse -Directory -Force -ErrorAction SilentlyContinue |
Where-Object LastAccessTime -le ((get-date).AddDays($KEEP))
Remove-Item -Recurse -Force $_ -Whatif

Delete files older than 15 days using PowerShell

I would like to delete only the files that were created more than 15 days ago in a particular folder. How could I do this using PowerShell?
The given answers will only delete files (which admittedly is what is in the title of this post), but here's some code that will first delete all of the files older than 15 days, and then recursively delete any empty directories that may have been left behind. My code also uses the -Force option to delete hidden and read-only files as well. Also, I chose to not use aliases as the OP is new to PowerShell and may not understand what gci, ?, %, etc. are.
$limit = (Get-Date).AddDays(-15)
$path = "C:\Some\Path"
# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
# Delete any empty directories left behind after deleting the old files.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse
And of course if you want to see what files/folders will be deleted before actually deleting them, you can just add the -WhatIf switch to the Remove-Item cmdlet call at the end of both lines.
If you only want to delete files that haven't been updated in 15 days, vs. created 15 days ago, then you can use $_.LastWriteTime instead of $_.CreationTime.
The code shown here is PowerShell v2.0 compatible, but I also show this code and the faster PowerShell v3.0 code as handy reusable functions on my blog.
just simply (PowerShell V5)
Get-ChildItem "C:\temp" -Recurse -File | Where CreationTime -lt (Get-Date).AddDays(-15) | Remove-Item -Force
Another way is to subtract 15 days from the current date and compare CreationTime against that value:
$root = 'C:\root\folder'
$limit = (Get-Date).AddDays(-15)
Get-ChildItem $root -Recurse | ? {
-not $_.PSIsContainer -and $_.CreationTime -lt $limit
} | Remove-Item
Basically, you iterate over files under the given path, subtract the CreationTime of each file found from the current time, and compare against the Days property of the result. The -WhatIf switch will tell you what will happen without actually deleting the files (which files will be deleted), remove the switch to actually delete the files:
$old = 15
$now = Get-Date
Get-ChildItem $path -Recurse |
Where-Object {-not $_.PSIsContainer -and $now.Subtract($_.CreationTime).Days -gt $old } |
Remove-Item -WhatIf
Try this:
dir C:\PURGE -recurse |
where { ((get-date)-$_.creationTime).days -gt 15 } |
remove-item -force
Esperento57's script doesn't work in older PowerShell versions. This example does:
Get-ChildItem -Path "C:\temp" -Recurse -force -ErrorAction SilentlyContinue | where {($_.LastwriteTime -lt (Get-Date).AddDays(-15) ) -and (! $_.PSIsContainer)} | select name| Remove-Item -Verbose -Force -Recurse -ErrorAction SilentlyContinue
If you are having problems with the above examples on a Windows 10 box, try replacing .CreationTime with .LastwriteTime. This worked for me.
dir C:\locationOfFiles -ErrorAction SilentlyContinue | Where { ((Get-Date)-$_.LastWriteTime).days -gt 15 } | Remove-Item -Force
Another alternative (15. gets typed to [timespan] automatically):
ls -file | where { (get-date) - $_.creationtime -gt 15. } | Remove-Item -Verbose
#----- Define parameters -----#
#----- Get current date ----#
$Now = Get-Date
$Days = "15" #----- define amount of days ----#
$Targetfolder = "C:\Logs" #----- define folder where files are located ----#
$Extension = "*.log" #----- define extension ----#
$Lastwrite = $Now.AddDays(-$Days)
#----- Get files based on lastwrite filter and specified folder ---#
$Files = Get-Childitem $Targetfolder -include $Extension -Recurse | where {$_.LastwriteTime -le "$Lastwrite"}
foreach ($File in $Files)
{
if ($File -ne $Null)
{
write-host "Deleting File $File" backgroundcolor "DarkRed"
Remove-item $File.Fullname | out-null
}
else {
write-host "No more files to delete" -forgroundcolor "Green"
}
}
$limit = (Get-Date).AddDays(-15)
$path = "C:\Some\Path"
# Delete files older than the $limit.
Get-ChildItem -Path $path -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force -Recurse
This will delete old folders and it content.
The following code will delete files older than 15 days in a folder.
$Path = 'C:\Temp'
$Daysback = "-15"
$CurrentDate = Get-Date
$DatetoDelete = $CurrentDate.AddDays($Daysback)
Get-ChildItem $Path -Recurse | Where-Object { $_.LastWriteTime -lt $DatetoDelete } | Remove-Item

Count deleted empty folders

I have a script right now that looks for all files certain day old and certain file extension and it deletes all of the files. This works fine and it counts fine
Then I have to delete all folders that correspond to being empty and that includes all sub folders too.
I also have to output this into a file and display each file deleted. The output would show 30 folders deleted but actually 48 were really deleted.
Now my question is i am trying to do a count of all the folders deleted. I have this script but it just counts the deepest folders not all the ones deleted.
Here is the part of the script i can not get to count
$TargetFolder = "C:\Users\user\Desktop\temp"
$LogFile = "C:\Summary.txt"
$Count = 0
Date | Out-File -filepath $LogFile
get-childitem $TargetFolder -recurse -force | Where-Object {$_.psIsContainer}| sort fullName -des |
Where-Object {!(get-childitem $_.fullName -force)} | ForEach-Object{$Count++; $_.fullName} | remove-item -whatif | Out-File -filepath $LogFile -append
$Count = "Total Folders = " + $Count
$Count | Out-File -filepath $LogFile -append
Although the sort call will correctly send each directory through the pipeline in nesting order, since they are not really being removed (remove-item -whatif), the parents will still contain their empty child directories and so will not pass the second condition (!(get-childitem $_.fullName -force)). Also note that Remove-Item does not produce any output, so the deleted directories will not appear in the log.
Adapting Keith Hill's answer to a similar question, here is a modified version of the original script that uses a filter to retrieve all empty directories first, then removes and logs each one:
filter Where-Empty {
$children = #($_ |
Get-ChildItem -Recurse -Force |
Where-Object { -not $_.PSIsContainer })
if( $_.PSIsContainer -and $children.Length -eq 0 ) {
$_
}
}
$emptyDirectories = #(
Get-ChildItem $TargetFolder -Recurse -Force |
Where-Empty |
Sort-Object -Property FullName -Descending)
$emptyDirectories | ForEach-Object {
$_ | Remove-Item -WhatIf -Recurse
$_.FullName | Out-File -FilePath $LogFile -Append
}
$Count = $emptyDirectories.Count
"Total Folders = $Count" | Out-File -FilePath $LogFile -Append
Note that -Recurse was added to the call to Remove-Item, as empty child directories will remain when using -WhatIf. Neither flag should be needed when performing an actual remove on an empty directory.
Not tested:
get-childitem $TargetFolder -recurse -force |
where-object{$_.psiscontainer -and -not (get-childitem $_.fullname -recurse -force | where-object {!($_.psiscontainer)}}|
sort fullName -des |
Where-Object {!(get-childitem $.fullName -force)} |
ForEach-Object{$Count++; $_.fullName} |
remove-item -whatif |
Out-File -filepath $LogFile -append