File & Folder Management
Note
Create, copy, move, rename, and delete files and folders with New-Item, Copy-Item, Move-Item, Remove-Item, and Test-Path.
Overview
Every script that touches the filesystem eventually needs to create a folder, copy a file, or clean up old data. PowerShell handles all of this with a small set of *-Item cmdlets that work identically across files, folders, and even other providers (registry, certificates). Once you know New-Item, Copy-Item, Move-Item, Remove-Item, and Test-Path, you can manage the filesystem without ever touching cmd.exe.
Basic Syntax
New-Item -Path "C:\Temp\report.txt" -ItemType File
Copy-Item -Path "C:\Source\file.txt" -Destination "C:\Backup\"
Move-Item -Path "C:\Temp\old.log" -Destination "C:\Archive\"
Remove-Item -Path "C:\Temp\file.txt"
Test-Path -Path "C:\Temp\file.txt"
Key Points
Test-Pathbefore you assume a file or folder exists — never guess-WhatIfpreviews destructive operations without running them-Recurseis required to remove non-empty folders-Forceoverwrites read-only or hidden items, and creates missing parent folders- All of these cmdlets work the same way against files and folders — the difference is
-ItemType
Creating Files and Folders
New-Item
# Create a folder
New-Item -Path "C:\Temp\Reports" -ItemType Directory
# Create an empty file
New-Item -Path "C:\Temp\Reports\output.txt" -ItemType File
# Create a file with initial content
New-Item -Path "C:\Temp\Reports\log.txt" -ItemType File -Value "Log started $(Get-Date)`n"
# Create nested folders in one call (parent folders don't need to exist)
New-Item -Path "C:\Temp\Reports\2026\July" -ItemType Directory -Force
Output:
Directory: C:\Temp\Reports\2026
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 7/1/2026 9:12 AM July
-Force Doesn't Just Overwrite
On New-Item, -Force also creates any missing parent directories. New-Item -Path "C:\A\B\C" -ItemType Directory -Force creates A, B, and C in one call — you don't need to build the path level by level.
Copying Files and Folders
Copy-Item
# Copy a single file
Copy-Item -Path "C:\Source\report.txt" -Destination "C:\Backup\report.txt"
# Copy a file to a folder (keeps original filename)
Copy-Item -Path "C:\Source\report.txt" -Destination "C:\Backup\"
# Copy an entire folder and its contents
Copy-Item -Path "C:\Source\Data" -Destination "C:\Backup\Data" -Recurse
# Overwrite existing files without prompting
Copy-Item -Path "C:\Source\*.csv" -Destination "C:\Backup\" -Force
Copy Multiple Files with Filtering
# Copy only .log files
Copy-Item -Path "C:\Source\*.log" -Destination "C:\Backup\Logs\"
# Copy files matching a pattern, recursively, preserving structure
Get-ChildItem -Path "C:\Source" -Filter "*.config" -Recurse | ForEach-Object {
$destination = $_.FullName.Replace("C:\Source", "C:\Backup")
$destFolder = Split-Path -Path $destination -Parent
if (-not (Test-Path $destFolder)) {
New-Item -Path $destFolder -ItemType Directory -Force | Out-Null
}
Copy-Item -Path $_.FullName -Destination $destination
}
Copy-Item -Recurse Doesn't Merge by Default
Copying a folder that already exists at the destination nests it inside instead of merging contents. Copy-Item -Path "C:\Source" -Destination "C:\Backup" -Recurse when C:\Backup already exists produces C:\Backup\Source\..., not a merge of contents. Use Robocopy (via robocopy.exe) if you need true mirror/merge semantics.
Moving and Renaming
Move-Item
# Move a file to a new folder
Move-Item -Path "C:\Temp\report.txt" -Destination "C:\Archive\"
# Move and rename in one step
Move-Item -Path "C:\Temp\report.txt" -Destination "C:\Archive\report-2026-07.txt"
# Move an entire folder
Move-Item -Path "C:\Temp\OldProject" -Destination "C:\Archive\OldProject"
# Move all files older than 30 days
Get-ChildItem -Path "C:\Temp" -File | Where-Object {
$_.LastWriteTime -lt (Get-Date).AddDays(-30)
} | Move-Item -Destination "C:\Archive\"
Rename-Item
# Rename a file (stays in the same folder)
Rename-Item -Path "C:\Temp\old-name.txt" -NewName "new-name.txt"
# Rename a folder
Rename-Item -Path "C:\Temp\Draft" -NewName "Final"
# Bulk rename: add a prefix to every file in a folder
Get-ChildItem -Path "C:\Temp\Photos" -File | Rename-Item -NewName {
"vacation-" + $_.Name
}
Deleting Files and Folders
Remove-Item
# Delete a single file
Remove-Item -Path "C:\Temp\report.txt"
# Delete an empty folder
Remove-Item -Path "C:\Temp\EmptyFolder"
# Delete a folder and everything inside it
Remove-Item -Path "C:\Temp\OldProject" -Recurse -Force
# Delete all .tmp files in a folder tree
Get-ChildItem -Path "C:\Temp" -Filter "*.tmp" -Recurse | Remove-Item -Force
Removing a Folder Without -Recurse
# BAD - Fails (or prompts) if the folder isn't empty
Remove-Item -Path "C:\Temp\Reports"
# GOOD - Removes the folder and everything in it
Remove-Item -Path "C:\Temp\Reports" -Recurse -Force
Remove-Item — deletions are permanent. Always test with -WhatIf first on anything scripted. Preview Before You Delete
# Shows what WOULD be deleted without deleting anything
Remove-Item -Path "C:\Temp\*.log" -WhatIf
# Prompt for confirmation on each item
Remove-Item -Path "C:\Temp\*.log" -Confirm
Output (-WhatIf):
What if: Performing the operation "Remove File" on target "C:\Temp\error.log".
What if: Performing the operation "Remove File" on target "C:\Temp\debug.log".
Checking Existence with Test-Path
# Basic existence check
if (Test-Path -Path "C:\Temp\config.json") {
Write-Output "Config found"
}
# Check that a path exists AND is a specific type
Test-Path -Path "C:\Temp\Reports" -PathType Container # folder
Test-Path -Path "C:\Temp\report.txt" -PathType Leaf # file
# Guard pattern: create only if missing
$logFolder = "C:\Logs"
if (-not (Test-Path -Path $logFolder)) {
New-Item -Path $logFolder -ItemType Directory | Out-Null
}
Test-Path Never Throws
Unlike trying to access a missing path directly, Test-Path always returns $true or $false — it never throws an error, even for paths that are completely invalid or unreachable. That makes it the safe way to guard filesystem operations before they run.
Important Parameters
| Parameter | Type | Description | Example |
|---|---|---|---|
-Path | String | Source path for the operation | Copy-Item -Path "C:\a.txt" |
-Destination | String | Target path for Copy/Move | Move-Item -Destination "C:\Archive\" |
-ItemType | String | File, Directory, SymbolicLink | New-Item -ItemType Directory |
-Recurse | Switch | Include subfolders/contents | Remove-Item -Recurse |
-Force | Switch | Overwrite, bypass read-only, create parents | Copy-Item -Force |
-WhatIf | Switch | Preview without executing | Remove-Item -WhatIf |
-Confirm | Switch | Prompt before each action | Remove-Item -Confirm |
-PathType | String | For Test-Path: Leaf, Container, Any | Test-Path -PathType Leaf |
Common Patterns
# Pattern 1: Safe cleanup — only delete files older than N days
Get-ChildItem -Path "C:\Temp" -File -Recurse |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) } |
Remove-Item -Force -WhatIf # remove -WhatIf once you trust the output
# Pattern 2: Ensure a folder exists before writing to it
$outputPath = "C:\Reports\2026\July"
if (-not (Test-Path $outputPath)) {
New-Item -Path $outputPath -ItemType Directory -Force | Out-Null
}
# Pattern 3: Timestamped backup copy before overwriting
$file = "C:\Config\settings.json"
if (Test-Path $file) {
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
Copy-Item -Path $file -Destination "$file.$timestamp.bak"
}
Real-World Examples
Example: Nightly Log Rotation
Scenario: Move logs older than 1 day into a dated archive folder, then delete anything older than 90 days.
$logSource = "C:\App\Logs"
$archiveRoot = "C:\App\Archive"
$today = Get-Date -Format "yyyy-MM-dd"
# Archive yesterday's logs
$archiveFolder = Join-Path $archiveRoot $today
if (-not (Test-Path $archiveFolder)) {
New-Item -Path $archiveFolder -ItemType Directory -Force | Out-Null
}
Get-ChildItem -Path $logSource -Filter "*.log" | Where-Object {
$_.LastWriteTime -lt (Get-Date).Date
} | Move-Item -Destination $archiveFolder
# Purge archives older than 90 days
Get-ChildItem -Path $archiveRoot -Directory | Where-Object {
$_.CreationTime -lt (Get-Date).AddDays(-90)
} | Remove-Item -Recurse -Force
Explanation: Test-Path guards folder creation so the script is safe to re-run, Where-Object filters by date on both ends, and -Recurse -Force handles the actual purge of stale archive folders.
Tips & Tricks
Always -WhatIf Before Bulk Deletes
# Run this first to see exactly what will be removed
Remove-Item -Path "C:\Temp\*.tmp" -Recurse -WhatIf
# Then run it for real once the output looks right
Remove-Item -Path "C:\Temp\*.tmp" -Recurse -Force
-WhatIf works on every cmdlet that supports ShouldProcess — that includes New-Item, Copy-Item, Move-Item, and Remove-Item. Wildcards Behave Differently on -Path vs -Destination
# BAD - wildcard destination folder that doesn't exist yet fails silently in some cases
Copy-Item -Path "C:\Source\*.txt" -Destination "C:\Backup\*"
# GOOD - point -Destination at an actual existing folder
Copy-Item -Path "C:\Source\*.txt" -Destination "C:\Backup\"
-Path. -Destination should be a real folder or file path. Related Topics
- Path Operations - Building and validating paths before you use them
- Reading & Writing Files - Working with file contents once you've located them
- Searching Files - Finding the files to copy, move, or delete