PowerShell provides powerful cmdlets for managing files and folders, making it an essential tool for system administrators and developers. The three core cmdlets—Get-ChildItem, Copy-Item, and Remove-Item—form the foundation of file system management in PowerShell. This comprehensive guide explores these cmdlets with practical examples and real-world scenarios.
Understanding Get-ChildItem: Listing Files and Folders
Get-ChildItem is PowerShell’s equivalent to the dir or ls commands. It retrieves items from specified locations, typically files and folders, and offers extensive filtering capabilities.
Basic Syntax and Usage
The simplest form of Get-ChildItem lists all items in the current directory:
Get-ChildItem
Visual output:
Directory: C:\Users\YourName\Documents
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/15/2025 2:30 PM Projects
d----- 10/18/2025 9:45 AM Scripts
-a---- 10/20/2025 1:15 PM 2048 report.txt
-a---- 10/22/2025 10:30 AM 15360 data.xlsx
The alias gci or traditional dir and ls can also be used:
gci
dir
ls
Specifying Paths and Locations
Target specific directories by providing a path:
Get-ChildItem -Path "C:\Windows\System32"
Get-ChildItem -Path ".\Projects" # Relative path
Recursive Directory Traversal
The -Recurse parameter searches through all subdirectories:
Get-ChildItem -Path "C:\Projects" -Recurse
This searches the entire directory tree, listing all nested files and folders.
Filtering by File Type and Name
Use the -Filter parameter for efficient wildcard matching:
# Find all PowerShell scripts
Get-ChildItem -Path "C:\Scripts" -Filter "*.ps1"
# Find all text files
Get-ChildItem -Filter "*.txt"
# Find files starting with "report"
Get-ChildItem -Filter "report*"
Example output:
Directory: C:\Scripts
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 10/10/2025 3:20 PM 1024 backup.ps1
-a---- 10/12/2025 4:15 PM 2560 deploy.ps1
-a---- 10/20/2025 11:00 AM 768 cleanup.ps1
Advanced Filtering with -Include and -Exclude
For multiple patterns or exclusions, use -Include and -Exclude:
# Include multiple file types
Get-ChildItem -Path "C:\Projects" -Recurse -Include "*.ps1", "*.json", "*.xml"
# Exclude specific patterns
Get-ChildItem -Path "C:\Logs" -Exclude "*.tmp", "*.bak"
# Combine both
Get-ChildItem -Recurse -Include "*.log" -Exclude "*debug*"
Filtering by Attributes
Filter files based on attributes like hidden, system, or read-only:
# Find hidden files
Get-ChildItem -Hidden
# Find directories only
Get-ChildItem -Directory
# Find files only
Get-ChildItem -File
# Find read-only files
Get-ChildItem -Attributes ReadOnly
Working with File Properties
Access detailed properties using the pipeline:
# Get files larger than 1MB
Get-ChildItem -File | Where-Object { $_.Length -gt 1MB }
# Find files modified in the last 7 days
Get-ChildItem -File | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) }
# Sort by size descending
Get-ChildItem -File | Sort-Object Length -Descending | Select-Object Name, Length
Example output with size filtering:
Name Length
---- ------
database.bak 52428800
archive.zip 15728640
backup.tar 8388608
Copy-Item: Copying Files and Folders
Copy-Item duplicates files and directories from one location to another, supporting wildcards and recursive operations.
Basic File Copying
# Copy a single file
Copy-Item -Path "C:\Source\report.txt" -Destination "C:\Backup\"
# Copy and rename
Copy-Item -Path ".\config.json" -Destination ".\config.backup.json"
# Copy using alias
cp "file.txt" "file_copy.txt"
copy "document.docx" "D:\Backup\"
Copying Multiple Files
# Copy all text files
Copy-Item -Path "C:\Source\*.txt" -Destination "C:\Backup\"
# Copy specific file types
Copy-Item -Path ".\*.ps1" -Destination "D:\Scripts\"
Recursive Directory Copying
Copy entire directory structures with -Recurse:
# Copy folder and all contents
Copy-Item -Path "C:\Projects\WebApp" -Destination "C:\Backup\WebApp" -Recurse
# Copy contents of a folder
Copy-Item -Path "C:\Source\*" -Destination "C:\Destination\" -Recurse
Force and Overwrite Options
Handle existing files with the -Force parameter:
# Overwrite existing files without prompting
Copy-Item -Path ".\source.txt" -Destination ".\dest.txt" -Force
# Copy hidden/system files
Copy-Item -Path "C:\Source\*" -Destination "C:\Backup\" -Force -Recurse
Filtering During Copy
Combine Copy-Item with filtering:
# Copy only specific file types
Get-ChildItem -Path "C:\Source" -Filter "*.log" | Copy-Item -Destination "C:\Logs\"
# Copy files modified in last 24 hours
Get-ChildItem -File | Where-Object { $_.LastWriteTime -gt (Get-Date).AddHours(-24) } |
Copy-Item -Destination "C:\Recent\"
Container and Literal Path Options
# Copy just the files, not the container folder
Copy-Item -Path "C:\Source\*" -Destination "C:\Dest\" -Recurse
# Use literal path for special characters
Copy-Item -LiteralPath "C:\Folder[1]\file.txt" -Destination "C:\Backup\"
Practical Copy Scenarios
# Backup script files with timestamp
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
Copy-Item -Path "C:\Scripts\*.ps1" -Destination "C:\Backup\Scripts_$timestamp\"
# Copy and maintain directory structure
Get-ChildItem -Path "C:\Projects" -Include "*.config" -Recurse |
ForEach-Object {
$dest = $_.FullName.Replace("C:\Projects", "C:\Backup")
$destDir = Split-Path $dest
if (!(Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force }
Copy-Item $_.FullName -Destination $dest
}
Remove-Item: Deleting Files and Folders
Remove-Item permanently deletes files and folders. Use with caution as deletions are typically not recoverable from the command line.
Basic File Deletion
# Delete a single file
Remove-Item -Path "C:\Temp\oldfile.txt"
# Delete using alias
rm "file.txt"
del "document.docx"
Warning: Files deleted with Remove-Item bypass the Recycle Bin and are permanently removed.
Deleting Multiple Files
# Delete all text files
Remove-Item -Path "C:\Temp\*.txt"
# Delete specific patterns
Remove-Item -Path ".\temp_*"
Remove-Item -Path ".\*.tmp", ".\*.bak"
Recursive Deletion
Delete directories and all contents:
# Delete folder and everything inside
Remove-Item -Path "C:\OldProject" -Recurse
# Delete contents but keep folder
Remove-Item -Path "C:\Logs\*" -Recurse
Force Deletion
Override read-only and hidden attributes:
# Force delete read-only files
Remove-Item -Path ".\readonly.txt" -Force
# Force delete hidden files and folders
Remove-Item -Path "C:\HiddenFolder" -Recurse -Force
Confirmation and WhatIf
Preview or confirm deletions before executing:
# Preview what would be deleted (doesn't actually delete)
Remove-Item -Path "C:\Temp\*" -Recurse -WhatIf
# Prompt for confirmation
Remove-Item -Path ".\important_*" -Confirm
Example -WhatIf output:
What if: Performing the operation "Remove File" on target "C:\Temp\file1.txt".
What if: Performing the operation "Remove File" on target "C:\Temp\file2.log".
What if: Performing the operation "Remove Directory" on target "C:\Temp\OldFolder".
Safe Deletion Patterns
# Delete files older than 30 days
Get-ChildItem -Path "C:\Logs" -Recurse -File |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -Force
# Delete empty directories
Get-ChildItem -Path "C:\Projects" -Recurse -Directory |
Where-Object { (Get-ChildItem $_.FullName).Count -eq 0 } |
Remove-Item -Force
# Delete specific file types older than a date
Get-ChildItem -Path ".\Temp" -Filter "*.tmp" |
Where-Object { $_.CreationTime -lt (Get-Date "2025-01-01") } |
Remove-Item -WhatIf # Use -WhatIf first to verify
Exclude Patterns During Deletion
# Delete all except certain files
Get-ChildItem -Path "C:\Temp" -Exclude "*.config", "*.json" | Remove-Item -Recurse
# Delete logs but keep the latest
Get-ChildItem -Path ".\Logs\*.log" |
Sort-Object LastWriteTime -Descending |
Select-Object -Skip 5 |
Remove-Item
Combining Cmdlets for Advanced Operations
The real power of PowerShell emerges when combining these cmdlets with pipelines and other commands.
Move Operation (Copy + Remove)
PowerShell has Move-Item, but you can achieve it with copy and remove:
# Move files (using Move-Item)
Move-Item -Path ".\source.txt" -Destination ".\Archive\"
# Manual move with validation
$source = "C:\Source\file.txt"
$dest = "C:\Archive\file.txt"
Copy-Item -Path $source -Destination $dest
if (Test-Path $dest) {
Remove-Item -Path $source
}
Backup and Cleanup Workflow
# Create dated backup folder
$backupPath = "C:\Backups\Backup_$(Get-Date -Format 'yyyyMMdd')"
New-Item -ItemType Directory -Path $backupPath -Force
# Copy important files
Get-ChildItem -Path "C:\Projects" -Include "*.config", "*.json" -Recurse |
Copy-Item -Destination $backupPath -Force
# Remove old backups (older than 30 days)
Get-ChildItem -Path "C:\Backups" -Directory |
Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -Recurse -Force
Organize Files by Extension
# Organize downloads by file type
$extensions = @("*.pdf", "*.docx", "*.xlsx", "*.jpg", "*.png")
$basePath = "C:\Users\YourName\Downloads"
foreach ($ext in $extensions) {
$folderName = $ext.Replace("*.", "").ToUpper() + "_Files"
$destPath = Join-Path $basePath $folderName
if (!(Test-Path $destPath)) {
New-Item -ItemType Directory -Path $destPath
}
Get-ChildItem -Path $basePath -Filter $ext |
Move-Item -Destination $destPath
}
Synchronization Script
# Simple sync: copy newer files from source to destination
$source = "C:\Projects\Active"
$dest = "C:\Backup\Projects"
Get-ChildItem -Path $source -Recurse -File | ForEach-Object {
$sourcePath = $_.FullName
$destPath = $sourcePath.Replace($source, $dest)
$destDir = Split-Path $destPath
# Create destination directory if needed
if (!(Test-Path $destDir)) {
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
}
# Copy if destination doesn't exist or source is newer
if (!(Test-Path $destPath) -or $_.LastWriteTime -gt (Get-Item $destPath).LastWriteTime) {
Copy-Item -Path $sourcePath -Destination $destPath -Force
Write-Host "Synced: $($_.Name)" -ForegroundColor Green
}
}
Find and Archive Large Files
# Find files larger than 100MB and move to archive
$threshold = 100MB
$archivePath = "D:\Archive"
Get-ChildItem -Path "C:\Projects" -Recurse -File |
Where-Object { $_.Length -gt $threshold } |
ForEach-Object {
Write-Host "Archiving: $($_.Name) ($([math]::Round($_.Length/1MB, 2)) MB)"
Move-Item -Path $_.FullName -Destination $archivePath
}
Error Handling and Best Practices
Using Try-Catch Blocks
try {
Get-ChildItem -Path "C:\NonExistent" -ErrorAction Stop
} catch {
Write-Host "Error: $_" -ForegroundColor Red
}
# Copy with error handling
$files = Get-ChildItem -Path "C:\Source" -Filter "*.txt"
foreach ($file in $files) {
try {
Copy-Item -Path $file.FullName -Destination "C:\Backup\" -ErrorAction Stop
Write-Host "Copied: $($file.Name)" -ForegroundColor Green
} catch {
Write-Host "Failed to copy $($file.Name): $_" -ForegroundColor Red
}
}
Testing Path Existence
# Check before operations
if (Test-Path "C:\Source\file.txt") {
Copy-Item -Path "C:\Source\file.txt" -Destination "C:\Backup\"
} else {
Write-Host "Source file not found" -ForegroundColor Yellow
}
# Ensure destination directory exists
$destPath = "C:\Backup\NewFolder"
if (!(Test-Path $destPath)) {
New-Item -ItemType Directory -Path $destPath -Force
}
Copy-Item -Path ".\files\*" -Destination $destPath
Performance Considerations
# Efficient: Use -Filter at cmdlet level
Get-ChildItem -Path "C:\Large" -Filter "*.log" -Recurse
# Less efficient: Filtering after retrieval
Get-ChildItem -Path "C:\Large" -Recurse | Where-Object { $_.Extension -eq ".log" }
# Batch operations for better performance
$files = Get-ChildItem -Path "C:\Source" -Filter "*.txt"
$files | Copy-Item -Destination "C:\Backup\" -Force
Logging Operations
# Create operation log
$logFile = "C:\Logs\file_operations_$(Get-Date -Format 'yyyyMMdd').log"
function Write-Log {
param($Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp - $Message" | Add-Content -Path $logFile
}
# Use in operations
Get-ChildItem -Path "C:\Source" | ForEach-Object {
try {
Copy-Item -Path $_.FullName -Destination "C:\Backup\" -Force
Write-Log "SUCCESS: Copied $($_.Name)"
} catch {
Write-Log "ERROR: Failed to copy $($_.Name) - $_"
}
}
Real-World Automation Scripts
Daily Backup Script
# Daily automated backup with rotation
$sourcePath = "C:\Important"
$backupRoot = "D:\Backups"
$date = Get-Date -Format "yyyyMMdd"
$backupPath = Join-Path $backupRoot "Backup_$date"
# Create backup directory
New-Item -ItemType Directory -Path $backupPath -Force | Out-Null
# Copy files
Write-Host "Starting backup..." -ForegroundColor Cyan
Get-ChildItem -Path $sourcePath -Recurse |
Copy-Item -Destination $backupPath -Recurse -Force
# Remove backups older than 7 days
Get-ChildItem -Path $backupRoot -Directory |
Where-Object { $_.Name -match "Backup_\d{8}" -and $_.CreationTime -lt (Get-Date).AddDays(-7) } |
Remove-Item -Recurse -Force
Write-Host "Backup completed: $backupPath" -ForegroundColor Green
Log Cleanup Script
# Clean old log files
$logPaths = @("C:\Logs\Application", "C:\Logs\System", "C:\Logs\Security")
$retentionDays = 30
foreach ($path in $logPaths) {
if (Test-Path $path) {
Write-Host "Cleaning logs in $path..." -ForegroundColor Cyan
$deletedCount = 0
$deletedSize = 0
Get-ChildItem -Path $path -Filter "*.log" -Recurse |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$retentionDays) } |
ForEach-Object {
$deletedSize += $_.Length
$deletedCount++
Remove-Item -Path $_.FullName -Force
}
Write-Host "Deleted $deletedCount files ($([math]::Round($deletedSize/1MB, 2)) MB)" -ForegroundColor Green
}
}
Directory Structure Report
# Generate directory size report
$targetPath = "C:\Projects"
$reportPath = "C:\Reports\directory_report_$(Get-Date -Format 'yyyyMMdd').csv"
$report = Get-ChildItem -Path $targetPath -Directory | ForEach-Object {
$size = (Get-ChildItem -Path $_.FullName -Recurse -File |
Measure-Object -Property Length -Sum).Sum
[PSCustomObject]@{
Directory = $_.Name
FullPath = $_.FullName
FileCount = (Get-ChildItem -Path $_.FullName -Recurse -File).Count
SizeMB = [math]::Round($size / 1MB, 2)
LastModified = $_.LastWriteTime
}
} | Sort-Object SizeMB -Descending
$report | Export-Csv -Path $reportPath -NoTypeInformation
$report | Format-Table -AutoSize
Common Patterns and Shortcuts
Quick Reference Commands
# List files by size
gci | sort Length -Descending | select Name, @{N="Size(MB)";E={[math]::Round($_.Length/1MB,2)}}
# Count files by extension
gci -File | group Extension | select Name, Count | sort Count -Descending
# Find duplicate filenames
gci -Recurse -File | group Name | where Count -gt 1 | select Name, Count
# Get total directory size
(gci -Recurse -File | measure Length -Sum).Sum / 1GB
# Find empty folders
gci -Recurse -Directory | where { (gci $_.FullName).Count -eq 0 }
# Recent files (last 24 hours)
gci -Recurse -File | where LastWriteTime -gt (Get-Date).AddHours(-24)
Useful Aliases
# Get-ChildItem aliases
gci, dir, ls
# Copy-Item aliases
copy, cp, cpi
# Remove-Item aliases
del, erase, rd, ri, rm, rmdir
# Example usage
ls | where Name -like "*.txt" | cp -Destination C:\Backup\
dir *.log | where Length -gt 1MB | rm -Force
Security Considerations
Always validate paths and implement safeguards when working with file operations:
# Validate user input
function Remove-SafeFolder {
param(
[Parameter(Mandatory)]
[string]$Path
)
# Prevent deletion of critical paths
$protectedPaths = @("C:\Windows", "C:\Program Files", "C:\Users")
foreach ($protected in $protectedPaths) {
if ($Path -like "$protected*") {
Write-Host "Cannot delete protected path: $Path" -ForegroundColor Red
return
}
}
if (Test-Path $Path) {
Remove-Item -Path $Path -Recurse -Force -Confirm
}
}
# Always use -WhatIf for destructive operations in scripts
$dryRun = $true
if ($dryRun) {
Remove-Item -Path $targetPath -Recurse -WhatIf
} else {
Remove-Item -Path $targetPath -Recurse -Force
}
Summary and Key Takeaways
Mastering Get-ChildItem, Copy-Item, and Remove-Item provides a solid foundation for PowerShell file management:
- Get-ChildItem is your primary tool for discovering and filtering files with extensive options for recursion, filtering, and attribute-based selection
- Copy-Item handles file and directory duplication with support for wildcards, recursion, and force operations
- Remove-Item permanently deletes files and folders—always use
-WhatIffirst with recursive operations - Combine cmdlets through pipelines for powerful automation workflows
- Implement error handling and logging for production scripts
- Use
-Filterat the cmdlet level for better performance on large directories - Always validate paths and implement safety checks for destructive operations
These three cmdlets form the cornerstone of file system automation in PowerShell. By combining them with conditional logic, loops, and other PowerShell features, you can create robust scripts for backup, cleanup, organization, and maintenance tasks. Practice with -WhatIf first, implement proper error handling, and your PowerShell file management skills will serve you well in system administration and development workflows.








