I'm pretty new to powershell.
I need to learn it though.
I started with a simple script that deletes certain file extensions in different paths, if they are older than 10 days.
$DeleteDate = (Get-Date).AddDays(-10)
$path1 = "\\path1\*.tibx*"
$path2 = "\\path2\*Backup Set*"
$path3 = "\\path3\*.tibx*"
Get-ChildItem $path1 | Where {$_.LastWriteTime -lt "$DeleteDate"} | Remove-Item
Get-ChildItem $path2 -Recurse | Where {$_.LastWriteTime -lt "$DeleteDate"} | Remove-Item
Get-ChildItem $path3 | Where {_.LastWriteTime -lt "$DeleteDate"} | Remove-Item
This works so far. Now what I would also need is, that it is logging, what and when it was deleted.
The only problem about this is, that Remove-Item doesnt seem to have output. Not even with "verbose".
I thought about putting the Items in an array, then write the array before and after the delete in a log file.
But this seems kinda complex for a task this simple.
Do you have any Ideas that could help me?
Insert ForEach-Object in the pipeline(s) just before Remove-Item, then log it there:
Get-ChildItem $path1 | Where {$_.LastWriteTime -lt "$DeleteDate"} | ForEach-Object {
Write-Verbose "Deleting '$($_.FullName)' at [$(Get-Date -Format o)]"
$_
} | Remove-Item
Related
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.
I am new to PowerShell and I have created the following code to delete specific files and folders:
$myFolderPath = "C:\Test"
$myLimit = (Get-Date).AddDays(-14)
# Delete files according to filter.
Get-ChildItem -Path $myFolderPath -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $myLimit} | Remove-Item -Force
# Delete empty folders.
Get-ChildItem -Path $myFolderPath -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse
Is it possible to print out the full path of each item that will be removed to the console before the actual Remove-Item operation will be performed?
I guess sth. has to be added here....
... | Remove-Item -Force
and here...
... | Remove-Item -Force -Recurse
but I cannot find out how to implement that in an elegant way (without code duplication).
You can replace the remove-Item-Parts with
Foreach-Object { $_.fullname; Remove-Item -Path $_.Fullname (-Recurse) -Force}
LotPings comment might be better idea, if that is what you want.
It does not get a lot of attention but Tee-Object could be a simple addition to the pipeline here. Redirect the output to a variable that you can print later.
...Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $myLimit} |
Tee-Object -Variable removed | Remove-Item -Force
$removed | Write-Host
All of the file objects piped will be sent to $removed and then to Remove-Item. Since you have more than one delete pipeline you can also use the -Append parameter so that all files are saved in one variable if you so desired.
However this does not mean they were deleted. Just they made it passed the pipe. If you really wanted to be sure you should be using another utility like robocopy which has logging features.
Am trying to delete files older than x days and would like to know which file is being deleted.
Am using below powershell script, it doesnt work
$limit = (Get-Date).AddDays(-365)
$path = $args[0]
# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force | select Name,LastWriteTime | Export-CSV -NoTypeInformation -Path $args[1]
Am passing first argument as path where files are there.
Second argument is the output file which should contain the file and date modified values of those which gets deleted.
The above code works fine for deletion, but doesnt redirects the file names and the last modified values which got deleted.
If I use below code, it only redirects the file names and last modified values but files doesnt get deleted.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | select Name,LastWriteTime | Export-CSV -NoTypeInformation -Path $args[1] | Remove-Item -Force
Using below command to run it -
./OlderFiles_Cleansing.ps1 'C:\Dev\PS' 'C:\dev\CleanedFiles_01062016.csv'
What am I missing?
Neither the Export-Csv nor the Remove-Item Cmdlet return the collection you pipe in and so make it impossible to work with the items further in the pipeline.
You can do following though - split the command:
$filesToDelete = Get-ChildItem -Path $path -Recurse -Force -Attributes !Directory | Where-Object CreationTime -lt $limit
$filesToDelete | select Name,LastWriteTime | Export-CSV -NoTypeInformation -Path $args[1]
$filesToDelete | Remove-Item -Force
Note I have improved the way of detecting that an item is a file using the
Attributes param and so could simplify the Where pipe part
I have a Powershell script that is supposed to delete items that are X days old. It doesnt fully work. It removes some files but not all of the files. When I run the script without | Remove-Item -Force, all the files that meet the requirements are displayed. So, I know the where statement works.
Why doesn't Remove-Item -Force not delete all items that meet the requirements set be the where statement, and how can it be fixed?
$deleteFiles = Get-Childitem $fullTargetPath -Recurse
| Where {$_.LastWriteTime -lt (Get-Date).AddDays(-10)} | Remove-Item -Force
Just before the "Remove-Item" add "Foreach".
So for example:
$deleteFiles = Get-Childitem $fullTargetPath -Recurse |
Where {$_.LastWriteTime -lt (Get-Date).AddDays(-10)} |
Foreach { Remove-Item $_.FullName -Force}
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