Skip to content

PowerShell Deleting Files

How to safely delete files in PowerShell using Remove-Item with -Force, -WhatIf, and -Confirm, including handling read-only files, wildcards, and permanent vs. Recycle Bin deletion.

Deleting files is a destructive, often irreversible operation. PowerShell centralizes file deletion through Remove-Item (aliases: rm, del, erase, ri) and provides built-in safety mechanisms, -WhatIf and -Confirm, to preview or confirm destructive actions before they happen. Understanding how to handle read-only files, preview mass removals, delete with wildcard patterns, and recognize the permanent nature of PowerShell deletions is essential to preventing accidental data loss.

Deleting Files with Remove-Item

Remove-Item deletes a file (or folder) at the specified path.

Basic Syntax

Remove-Item -Path <String[]> [-Force] [-Filter <String>] [-Recurse] [-WhatIf] [-Confirm]
Consolepowershell
Remove-Item -Path "C:\Temp\old-report.txt"

Where:

  • -Path specifies the file to delete.
  • By default, Remove-Item produces no confirmation or output when successful.
Consolepowershell
Test-Path "C:\Temp\old-report.txt"
Outputplaintext
False

A complete script form:

delete-single-file.ps1powershell
$TargetFile = "$env:TEMP\obsolete-cache.tmp"

$null = New-Item -ItemType File -Path $TargetFile -Force

Remove-Item -Path $TargetFile

Test-Path -Path $TargetFile
Consolepowershell
pwsh ./delete-single-file.ps1
Outputplaintext
False

Deleting Read-Only and Hidden Files with -Force

By default, Remove-Item fails with an UnauthorizedAccessException if a target file has the ReadOnly or Hidden file attribute set. Adding the -Force parameter overrides these attributes and removes the file:

Consolepowershell
Remove-Item -Path "C:\Configs\protected.lock" -Force

Safety Flags: -WhatIf and -Confirm

Because file deletions can be destructive and irreversible, Remove-Item supports PowerShell's ShouldProcess safety mechanisms: -WhatIf and -Confirm. Both are common parameters supported by Remove-Item and most other cmdlets that modify system state.

Simulating Deletion with -WhatIf

The -WhatIf parameter simulates the command without making any changes on disk. PowerShell prints the exact operation and file path it would have targeted.

Consolepowershell
Remove-Item -Path "C:\Temp\old-report.txt" -WhatIf
Outputplaintext
What if: Performing the operation "Remove File" on target "C:\Temp\old-report.txt".

No file is actually deleted: the output only describes the action that would occur.

Consolepowershell
Remove-Item -Path "C:\Logs\*.log" -WhatIf
Outputplaintext
What if: Performing the operation "Remove File" on target "C:\Logs\audit.log".
What if: Performing the operation "Remove File" on target "C:\Logs\trace.log".
What if: Performing the operation "Remove File" on target "C:\Logs\service.log".

Prompting for Interactive Confirmation with -Confirm

The -Confirm switch pauses execution and prompts the user before deleting each file.

Consolepowershell
Remove-Item -Path "C:\Temp\old-report.txt" -Confirm
Outputplaintext
Confirm
Are you sure you want to perform this action?
Performing the operation "Remove File" on target "C:\Temp\old-report.txt".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Where:

  • Typing Y and pressing Enter proceeds with the deletion; N cancels it.
  • A approves all remaining prompts; L denies all remaining prompts.

Suppressing Confirmation with -Confirm:$false

Some cmdlets prompt for confirmation automatically on high-impact operations. Pass -Confirm:$false to suppress the prompt in unattended/automated scripts where you've already validated the operation is safe.

Consolepowershell
Remove-Item -Path "C:\Temp\old-report.txt" -Confirm:$false

Deleting Files with Wildcards

Remove-Item supports standard PowerShell wildcards (*, ?) to delete groups of files matching specific naming patterns or extensions.

wildcard-deletion.ps1powershell
$DropZone = "$env:TEMP\CleanupDemo"

$null = New-Item -ItemType Directory -Path $DropZone -Force
$null = New-Item -ItemType File -Path "$DropZone\test1.tmp", "$DropZone\test2.tmp", "$DropZone\keep.txt"

Write-Output "Files before deletion:"
(Get-ChildItem -Path $DropZone).Name

Remove-Item -Path "$DropZone\*.tmp"

Write-Output "`nFiles after deletion:"
(Get-ChildItem -Path $DropZone).Name
Consolepowershell
pwsh ./wildcard-deletion.ps1
Outputplaintext
Files before deletion:
keep.txt
test1.tmp
test2.tmp

Files after deletion:
keep.txt

Where:

  • "$DropZone\*.tmp" matches all files in the target directory that have a .tmp extension.
  • Non-matching files (such as keep.txt) remain untouched on disk.

Combining Wildcards with Pipeline Filters

Combining Get-ChildItem, Where-Object, and Remove-Item deletes only files matching both a naming pattern and additional conditions, which is far more precise than a wildcard alone.

Consolepowershell
Get-ChildItem -Path "C:\Logs" -Filter "*.log" |
    Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
    Remove-Item -WhatIf
Outputplaintext
What if: Performing the operation "Remove File" on target "C:\Logs\january.log".
What if: Performing the operation "Remove File" on target "C:\Logs\february.log".

Permanent Deletion vs. The Recycle Bin

By default, Remove-Item permanently deletes files from disk.

Unlike deleting a file through Windows Explorer, PowerShell calls native low-level filesystem APIs (DeleteFile on Windows, unlink on Linux/macOS). The file bypasses the Windows Recycle Bin completely and cannot be restored from the desktop trash bin.

Windows Explorer Delete:
[File on Disk] ───► Moved to Recycle Bin / Trash ────► Recoverable

PowerShell Remove-Item:
[File on Disk] ───► Unlinked from File Table ────────► PERMANENTLY LOST
Method Goes to Recycle Bin? Recoverable?
Deleting via File Explorer Yes (by default) Yes, until Recycle Bin is emptied
Remove-Item No No, permanent by default

Sending Files to the Recycle Bin from PowerShell

Windows PowerShell and PowerShell 7 have no built-in cmdlet for Recycle Bin deletion. If an automation workflow requires a safety net where deleted files can be recovered by an end user, call the .NET Microsoft.VisualBasic.FileIO.FileSystem API instead of Remove-Item:

recycle-file.ps1powershell
Add-Type -AssemblyName Microsoft.VisualBasic

$FileToRecycle = "$env:TEMP\recoverable-doc.txt"
$null = New-Item -ItemType File -Path $FileToRecycle -Force

[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile(
    $FileToRecycle,
    'OnlyErrorDialogs',
    'SendToRecycleBin'
)

Write-Output "File moved to Windows Recycle Bin: $FileToRecycle"
Consolepowershell
pwsh ./recycle-file.ps1
Outputplaintext
File moved to Windows Recycle Bin: C:\Users\Tom\AppData\Local\Temp\recoverable-doc.txt

Where:

  • Add-Type -AssemblyName Microsoft.VisualBasic loads the assembly that interfaces with the Windows Shell.
  • DeleteFile(..., 'SendToRecycleBin') tells Windows to move the file into the user's active Recycle Bin rather than unlinking it immediately.

An alternate form using enum values directly:

recycle-delete.ps1powershell
Add-Type -AssemblyName Microsoft.VisualBasic

[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile(
    "C:\Temp\old-report.txt",
    [Microsoft.VisualBasic.FileIO.UIOption]::OnlyErrorDialogs,
    [Microsoft.VisualBasic.FileIO.RecycleOption]::SendToRecycleBin
)

Practical Examples

Safe Bulk Cleanup Script

A simple script for cleaning up old log files, with a caller-toggled -WhatIf preview mode:

cleanup-old-logs.ps1powershell
param(
    [string]$LogFolder = "C:\Logs",
    [int]$DaysOld = 30,
    [switch]$WhatIfMode
)

$filesToDelete = Get-ChildItem -Path $LogFolder -Filter "*.log" | Where-Object {
    $_.LastWriteTime -lt (Get-Date).AddDays(-$DaysOld)
}

Write-Output "Found $($filesToDelete.Count) file(s) older than $DaysOld days."

$filesToDelete | Remove-Item -WhatIf:$WhatIfMode -Confirm:$false
Consolepowershell
./cleanup-old-logs.ps1 -WhatIfMode
Outputplaintext
Found 2 file(s) older than 30 days.
What if: Performing the operation "Remove File" on target "C:\Logs\january.log".
What if: Performing the operation "Remove File" on target "C:\Logs\february.log".

Where:

  • -WhatIfMode is a custom [switch] parameter, letting the caller toggle preview mode without editing the script itself.
  • Running the script without -WhatIfMode performs the actual deletion.

Automated Temporary File Purge Engine

A more comprehensive script that cleans up temporary log files, targets files older than a specified number of days, checks for read-only flags, supports dry-run simulations, and emits a structured audit record:

purge-stale-logs.ps1powershell
param (
    [string]$TargetFolder = "$env:TEMP\LogDrop",
    [int]$DaysOld         = 7,
    [switch]$WhatIf
)

if (-not (Test-Path -Path $TargetFolder -PathType Container)) {
    Write-Warning "Directory '$TargetFolder' does not exist. Nothing to purge."
    exit 0
}

$CutoffDate = (Get-Date).AddDays(-$DaysOld)
Write-Output "Purging files older than: $($CutoffDate.ToString('yyyy-MM-dd HH:mm:ss'))"

$FilesToDelete = Get-ChildItem -Path $TargetFolder -Filter "*.log" -File |
    Where-Object { $_.LastWriteTime -lt $CutoffDate }

if ($FilesToDelete.Count -eq 0) {
    Write-Output "No log files found matching age criteria."
    exit 0
}

$AuditLog = foreach ($file in $FilesToDelete) {
    $fileAgeDays = [math]::Round(((Get-Date) - $file.LastWriteTime).TotalDays, 1)

    if ($WhatIf) {
        Remove-Item -Path $file.FullName -WhatIf
        $status = "Simulated"
    }
    else {
        Remove-Item -Path $file.FullName -Force -ErrorAction SilentlyContinue
        $status = if (Test-Path $file.FullName) { "Failed" } else { "Deleted" }
    }

    [PSCustomObject]@{
        FileName      = $file.Name
        SizeBytes     = $file.Length
        AgeInDays     = $fileAgeDays
        ActionStatus  = $status
        ExecutedAt    = (Get-Date -Format "HH:mm:ss")
    }
}

$AuditLog | Format-Table -AutoSize

Where:

  • $CutoffDate = (Get-Date).AddDays(-$DaysOld) uses date arithmetic to calculate the retention boundary.
  • Get-ChildItem ... | Where-Object { $_.LastWriteTime -lt $CutoffDate } isolates only files that exceed the retention period.
  • [switch]$WhatIf allows the script to be run in test mode without modifying any files.
  • Remove-Item -Force removes the files cleanly, including any marked with the ReadOnly attribute.
  • [PSCustomObject] produces an audit record of all purged files.

Execute the script in simulation mode:

Consolepowershell
pwsh ./purge-stale-logs.ps1 -TargetFolder "C:\AppLogs" -DaysOld 14 -WhatIf
Outputplaintext
Purging files older than: 2024-10-10 23:59:59
What if: Performing the operation "Remove File" on target "C:\AppLogs\trace-old.log".
What if: Performing the operation "Remove File" on target "C:\AppLogs\setup-2023.log".

FileName       SizeBytes AgeInDays ActionStatus ExecutedAt
--------       --------- --------- ------------ ----------
trace-old.log      45120      18.2 Simulated    23:59:12
setup-2023.log   1048576     365.1 Simulated    23:59:12

Run live execution without -WhatIf:

Consolepowershell
pwsh ./purge-stale-logs.ps1 -TargetFolder "C:\AppLogs" -DaysOld 14
Outputplaintext
Purging files older than: 2024-10-10 23:59:59

FileName       SizeBytes AgeInDays ActionStatus ExecutedAt
--------       --------- --------- ------------ ----------
trace-old.log      45120      18.2 Deleted      23:59:15
setup-2023.log   1048576     365.1 Deleted      23:59:15

Quick Reference

Task Command
Delete a single file Remove-Item -Path "C:\Temp\file.txt"
Delete read-only/hidden files Remove-Item -Path "C:\Temp\file.txt" -Force
Preview what would be deleted Remove-Item -Path "C:\Temp\*.tmp" -WhatIf
Prompt before deleting Remove-Item -Path "C:\Temp\file.txt" -Confirm
Suppress prompts in automation Remove-Item -Path $path -Confirm:$false
Delete files by wildcard Remove-Item -Path "C:\Logs\*.log"
Delete by pipeline filter Get-ChildItem ... | Where-Object { ... } | Remove-Item
Send to Recycle Bin (Windows) [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile(...)
Navigation