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]Remove-Item -Path "C:\Temp\old-report.txt"Where:
-Pathspecifies the file to delete.- By default,
Remove-Itemproduces no confirmation or output when successful.
Test-Path "C:\Temp\old-report.txt"FalseA complete script form:
$TargetFile = "$env:TEMP\obsolete-cache.tmp"
$null = New-Item -ItemType File -Path $TargetFile -Force
Remove-Item -Path $TargetFile
Test-Path -Path $TargetFilepwsh ./delete-single-file.ps1FalseDeleting 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:
Remove-Item -Path "C:\Configs\protected.lock" -ForceSafety 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.
Remove-Item -Path "C:\Temp\old-report.txt" -WhatIfWhat 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.
Remove-Item -Path "C:\Logs\*.log" -WhatIfWhat 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.
Remove-Item -Path "C:\Temp\old-report.txt" -ConfirmConfirm
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
Yand pressing Enter proceeds with the deletion;Ncancels it. Aapproves all remaining prompts;Ldenies 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.
Remove-Item -Path "C:\Temp\old-report.txt" -Confirm:$falseDeleting Files with Wildcards
Remove-Item supports standard PowerShell wildcards (*, ?) to delete groups of files matching specific naming patterns or extensions.
$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).Namepwsh ./wildcard-deletion.ps1Files before deletion:
keep.txt
test1.tmp
test2.tmp
Files after deletion:
keep.txtWhere:
"$DropZone\*.tmp"matches all files in the target directory that have a.tmpextension.- 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.
Get-ChildItem -Path "C:\Logs" -Filter "*.log" |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -WhatIfWhat 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:
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"pwsh ./recycle-file.ps1File moved to Windows Recycle Bin: C:\Users\Tom\AppData\Local\Temp\recoverable-doc.txtWhere:
Add-Type -AssemblyName Microsoft.VisualBasicloads 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:
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:
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./cleanup-old-logs.ps1 -WhatIfModeFound 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:
-WhatIfModeis a custom[switch]parameter, letting the caller toggle preview mode without editing the script itself.- Running the script without
-WhatIfModeperforms 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:
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 -AutoSizeWhere:
$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]$WhatIfallows the script to be run in test mode without modifying any files.Remove-Item -Forceremoves 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:
pwsh ./purge-stale-logs.ps1 -TargetFolder "C:\AppLogs" -DaysOld 14 -WhatIfPurging 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:12Run live execution without -WhatIf:
pwsh ./purge-stale-logs.ps1 -TargetFolder "C:\AppLogs" -DaysOld 14Purging 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:15Quick 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(...) |