Scripts

Script

Remove files older than N days

Delete files whose last write time is older than a threshold. Preview first.

intermediate files delete

Preview is the default. Pass -Apply to delete. Keep -WhatIf on the first real run.

A file that cannot be deleted (in use, read-only, or access denied) is skipped with a warning, and the run carries on. The last line says how many files were removed and how many failed. Add -Force to include read-only files.

.\remove-old-files.ps1 -Path .\logs -OlderThanDays 30 -Filter *.log
.\remove-old-files.ps1 -Path .\logs -OlderThanDays 30 -Filter *.log -Apply -WhatIf

Parameters

Name Type Required Description
-Path string Yes Folder to clean.
-OlderThanDays int Yes Age in days. Files last written before this cutoff are candidates.
-Filter string No Wildcard filter. Default *.
-Recurse switch No Include subfolders.
-Force switch No Also delete read-only files.
-Apply switch No Actually delete. Omit for a preview.

Source

remove-old-files.ps1
Download
<#
.SYNOPSIS
    Delete files older than a given number of days.

.DESCRIPTION
    Preview unless -Apply is passed. Use -WhatIf with -Apply.

.PARAMETER Path
    Folder to clean.

.PARAMETER OlderThanDays
    Files with LastWriteTime older than this many days are candidates.

.PARAMETER Filter
    Wildcard. Default *.

.PARAMETER Recurse
    Include subfolders.

.PARAMETER Force
    Also delete read-only files.

.PARAMETER Apply
    Perform deletes.

.EXAMPLE
    .\remove-old-files.ps1 -Path .\logs -OlderThanDays 30 -Filter *.log
#>
[CmdletBinding(SupportsShouldProcess)]
param(
    [Parameter(Mandatory)]
    [string]$Path,

    [Parameter(Mandatory)]
    [ValidateRange(1, 36500)]
    [int]$OlderThanDays,

    [string]$Filter = '*',

    [switch]$Recurse,

    [switch]$Force,

    [switch]$Apply
)

$ErrorActionPreference = 'Stop'

if (-not (Test-Path -LiteralPath $Path -PathType Container)) {
    throw "Folder not found: $Path"
}

$cutoff = (Get-Date).AddDays(-1 * $OlderThanDays)
# On Windows PowerShell 5.1, -Filter also matches 8.3 short names, so '*.htm'
# finds 'page.html'. Check the real name too. Brackets in -Filter are literal,
# so escape them for -like.
$namePattern = $Filter -replace '([\[\]])', '`$1'
$candidates = @(
    Get-ChildItem -LiteralPath $Path -File -Filter $Filter -Recurse:$Recurse -ErrorAction SilentlyContinue -ErrorVariable scanErrors |
        Where-Object { $_.Name -like $namePattern -and $_.LastWriteTime -lt $cutoff }
)

if ($scanErrors.Count -gt 0) {
    Write-Warning "$($scanErrors.Count) folder(s) skipped (access denied or unreadable)."
}

if (-not $Apply) {
    Write-Warning "Preview only. $($candidates.Count) file(s) older than $OlderThanDays day(s). Pass -Apply to delete."
    $candidates | Select-Object FullName, LastWriteTime, Length
    return
}

$removed = 0
$failed = 0
foreach ($item in $candidates) {
    if ($PSCmdlet.ShouldProcess($item.FullName, 'Remove-Item')) {
        try {
            Remove-Item -LiteralPath $item.FullName -Force:$Force
            $removed++
        } catch {
            $failed++
            Write-Warning "Could not delete $($item.FullName): $($_.Exception.Message)"
        }
    }
}

Write-Output ("Removed {0}, failed {1}." -f $removed, $failed)

Downloaded it? Windows blocks scripts from the internet until you unblock them: Unblock-File -Path .\remove-old-files.ps1. See Run a script.