Provider-Based File System Cmdlets: One Set of Verbs, Many Locations
PowerShell treats the file system as a provider. That means you use the same style of cmdlets (with consistent verbs like Get-, New-, Copy-, Remove-) to work with paths. In this chapter, we focus on everyday file and folder automation using these cmdlets: Get-ChildItem, Set-Location, New-Item, Copy-Item, Move-Item, Remove-Item, Rename-Item, and Test-Path.
We will follow a safety-first workflow: list what exists, check paths, then modify using -WhatIf first, and finally confirm results.
Safety-First Workflow for File Operations
1) List first with Get-ChildItem
Get-ChildItem (alias: gci, dir) lists files and folders. Start by inspecting what you’re about to change.
Get-ChildItem -Path $HOMETo list only folders:
Get-ChildItem -Path $HOME -DirectoryTo list only files:
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Get-ChildItem -Path $HOME -File2) Check paths before you touch anything with Test-Path
Test-Path returns True or False. Use it to avoid errors and to prevent creating/copying into the wrong place.
Test-Path -Path "$HOME\Desktop"Check whether a specific file exists:
Test-Path -Path "$HOME\Desktop\notes.txt"3) Use -WhatIf for “dry runs”
Many file-changing cmdlets support -WhatIf. It shows what would happen without making changes. Use it before Copy-Item, Move-Item, Remove-Item, and often Rename-Item.
Remove-Item -Path "$HOME\Desktop\old.log" -WhatIf4) Confirm results after changes
After a change, re-list the target folder (or test for a specific path) to verify the outcome.
Get-ChildItem -Path "$HOME\Desktop"Test-Path -Path "$HOME\Desktop\old.log"Set-Location: Move Your Working Context
Set-Location (alias: cd) changes the current working folder. This makes subsequent relative paths simpler and reduces mistakes from typing long paths repeatedly.
Set-Location -Path $HOMENavigate into a subfolder:
Set-Location -Path "$HOME\Desktop"Tip: When you’re doing a batch of operations in one folder, Set-Location helps keep commands short and readable.
Practice Workspace: Create a Safe Sandbox Folder
For the exercises, create a dedicated practice folder so you don’t accidentally modify real documents. The examples below use a folder on your Desktop, but you can choose any location.
Create the practice folder with New-Item
New-Item creates files and folders. Use -ItemType Directory for folders.
$PracticeRoot = Join-Path $HOME 'Desktop\PS-Files-Practice'if (-not (Test-Path -Path $PracticeRoot)) { New-Item -Path $PracticeRoot -ItemType Directory }Confirm:
Get-ChildItem -Path (Split-Path $PracticeRoot) -Directory | Where-Object Name -eq 'PS-Files-Practice'Exercise: Create subfolders
Create a simple structure for incoming files, processed files, and temporary files.
Set-Location -Path $PracticeRootNew-Item -Path . -Name 'Incoming' -ItemType DirectoryNew-Item -Path . -Name 'Processed' -ItemType DirectoryNew-Item -Path . -Name 'Temp' -ItemType DirectoryConfirm:
Get-ChildItem -Path . -DirectoryWildcard Patterns and Recursion
Wildcard patterns you’ll use constantly
Wildcards help you target groups of files without listing each one.
*matches any number of characters (including none). Example:*.logmatches all log files.?matches exactly one character. Example:file?.txtmatchesfile1.txtbut notfile10.txt.[abc]matches one character from a set. Example:report[12].csvmatchesreport1.csvandreport2.csv.[0-9]matches a range. Example:img[0-9].jpg.
List only certain files:
Get-ChildItem -Path .\Incoming -Filter '*.txt'Note: -Filter is handled by the file system provider and is often faster than filtering later. Use it when you can.
Recursion: include subfolders with -Recurse
Use -Recurse to search or operate through a folder tree.
Get-ChildItem -Path . -Recurse -FileBe careful with -Recurse on delete/move operations. Always dry-run first with -WhatIf.
Copy-Item and Move-Item: Safely Moving File Sets
Exercise setup: Create sample files
Create a few placeholder files so you can practice copying and renaming. New-Item can create empty files with -ItemType File.
1..5 | ForEach-Object { New-Item -Path .\Incoming -Name ("report_{0}.txt" -f $_) -ItemType File }1..3 | ForEach-Object { New-Item -Path .\Incoming -Name ("image_{0}.jpg" -f $_) -ItemType File }Confirm what you created:
Get-ChildItem -Path .\IncomingCopy a set of files (dry run first)
Copy only the text reports into Processed:
Copy-Item -Path .\Incoming\report_*.txt -Destination .\Processed -WhatIfIf the output looks correct, run it again without -WhatIf:
Copy-Item -Path .\Incoming\report_*.txt -Destination .\ProcessedConfirm:
Get-ChildItem -Path .\Processed -Filter 'report_*.txt'Move a set of files (dry run first)
Move the images into Temp:
Move-Item -Path .\Incoming\image_*.jpg -Destination .\Temp -WhatIfThen perform the move:
Move-Item -Path .\Incoming\image_*.jpg -Destination .\TempConfirm both sides:
Get-ChildItem -Path .\IncomingGet-ChildItem -Path .\TempRename-Item: Renaming with Patterns (Practical Approaches)
Rename-Item renames a file or folder. For multiple items, you typically combine Get-ChildItem with a loop so you can build a new name for each file.
Exercise: Add a prefix to all processed reports
Goal: rename report_1.txt to PROCESSED_report_1.txt (and so on) inside Processed.
1) List the target files first:
Get-ChildItem -Path .\Processed -Filter 'report_*.txt'2) Dry run the rename logic by printing the old/new names:
Get-ChildItem -Path .\Processed -Filter 'report_*.txt' | ForEach-Object { "{0} -> {1}" -f $_.Name, ("PROCESSED_{0}" -f $_.Name) }3) Rename (use -WhatIf if available in your environment; if not, keep the dry-run step above):
Get-ChildItem -Path .\Processed -Filter 'report_*.txt' | ForEach-Object { Rename-Item -Path $_.FullName -NewName ("PROCESSED_{0}" -f $_.Name) -WhatIf }4) Run for real (remove -WhatIf):
Get-ChildItem -Path .\Processed -Filter 'report_*.txt' | ForEach-Object { Rename-Item -Path $_.FullName -NewName ("PROCESSED_{0}" -f $_.Name) }Confirm:
Get-ChildItem -Path .\ProcessedExercise: Change file extensions safely
Sometimes you need to rename extensions (for example, .txt to .bak) for a subset of files. Always list first and be explicit about the pattern.
Dry run by showing the new names:
Get-ChildItem -Path .\Processed -Filter 'PROCESSED_*.txt' | ForEach-Object { "{0} -> {1}" -f $_.Name, ($_.BaseName + '.bak') }Rename:
Get-ChildItem -Path .\Processed -Filter 'PROCESSED_*.txt' | ForEach-Object { Rename-Item -Path $_.FullName -NewName ($_.BaseName + '.bak') }Confirm:
Get-ChildItem -Path .\ProcessedRemove-Item: Cleaning Up (Carefully)
Remove-Item deletes files and folders. This is where the safety-first approach matters most: list targets, use -WhatIf, then delete, then confirm.
Exercise: Create temp files, then remove only temp files
Create some temp files in Temp:
1..4 | ForEach-Object { New-Item -Path .\Temp -Name ("temp_{0}.tmp" -f $_) -ItemType File }List only .tmp files:
Get-ChildItem -Path .\Temp -Filter '*.tmp'Dry run deletion:
Remove-Item -Path .\Temp\*.tmp -WhatIfDelete for real:
Remove-Item -Path .\Temp\*.tmpConfirm:
Get-ChildItem -Path .\TempRemoving folders (and why -Recurse matters)
To remove a folder that contains items, you typically need -Recurse. Use -WhatIf first.
Remove-Item -Path .\Temp -Recurse -WhatIfIf correct, run without -WhatIf:
Remove-Item -Path .\Temp -RecurseConfirm the folder is gone:
Test-Path -Path .\TempSelecting Files by Time and Size (Everyday Maintenance Tasks)
Two common cleanup patterns are: “files older than X days” and “files larger than Y MB”. You can use LastWriteTime and Length (bytes) to target these.
Find files modified in the last 7 days
$Cutoff = (Get-Date).AddDays(-7)Get-ChildItem -Path $PracticeRoot -Recurse -File | Where-Object { $_.LastWriteTime -ge $Cutoff }Find files older than 30 days (candidate cleanup list)
$Cutoff = (Get-Date).AddDays(-30)Get-ChildItem -Path $PracticeRoot -Recurse -File | Where-Object { $_.LastWriteTime -lt $Cutoff }Find files larger than 10 MB
$MinBytes = 10MBGet-ChildItem -Path $PracticeRoot -Recurse -File | Where-Object { $_.Length -gt $MinBytes }Exercise: Dry-run delete of old .log files
This pattern is common in temp/log folders. First list what matches, then dry-run delete, then delete.
$Cutoff = (Get-Date).AddDays(-14)$Targets = Get-ChildItem -Path $PracticeRoot -Recurse -File -Filter '*.log' | Where-Object { $_.LastWriteTime -lt $Cutoff }$Targets | Select-Object FullName, LastWriteTime, Length$Targets | ForEach-Object { Remove-Item -Path $_.FullName -WhatIf }Putting It Together: A Repeatable “Inspect, Then Act” Checklist
| Goal | Inspect | Act (dry run) | Act (real) | Confirm |
|---|---|---|---|---|
| Copy a set of files | Get-ChildItem with -Filter | Copy-Item ... -WhatIf | Copy-Item ... | Get-ChildItem |
| Move a set of files | Get-ChildItem | Move-Item ... -WhatIf | Move-Item ... | Get-ChildItem + Test-Path |
| Rename many files | Get-ChildItem | Print old/new names | Rename-Item in a loop | Get-ChildItem |
| Delete temp files | Get-ChildItem | Remove-Item ... -WhatIf | Remove-Item ... | Get-ChildItem + Test-Path |
Final Exercise: Clean Up the Entire Practice Folder
When you’re done practicing, remove the entire sandbox folder. This is a good time to use -WhatIf and verify the path carefully.
1) Verify the folder exists and is the one you expect:
Test-Path -Path $PracticeRootGet-ChildItem -Path (Split-Path $PracticeRoot) -Directory | Where-Object Name -eq (Split-Path $PracticeRoot -Leaf)2) Dry run removal:
Remove-Item -Path $PracticeRoot -Recurse -WhatIf3) Remove for real:
Remove-Item -Path $PracticeRoot -Recurse4) Confirm it’s gone:
Test-Path -Path $PracticeRoot