PowerShell Get-ChildItem with Filter: Complete Guide [2024]
!PowerShell Get-ChildItem with Filter
The Get-ChildItem cmdlet is PowerShell’s most powerful file and directory search tool. Combined with filtering parameters like -Filter, -Include, and -Exclude, you can precisely locate files and folders in any directory structure.
Whether you’re managing log files, searching for configuration files, or auditing directories, mastering Get-ChildItem filtering is essential for efficient PowerShell scripting. This comprehensive guide covers everything from basic filtering to advanced techniques with real-world examples.
Table of Contents
- What is Get-ChildItem?
- Why Use Filtering?
- Filter vs Include vs Exclude
- Filter by File Extension
- Filter by Filename Pattern
- Using -Include Parameter
- Using -Exclude Parameter
- Recursive Search
- Files Only vs Directories Only
- Hidden and System Files
- Real-World Use Cases
- Performance Optimization
- Troubleshooting
- Best Practices
- FAQs
What is Get-ChildItem? {#what-is-get-childitem}
Get-ChildItem is PowerShell’s main cmdlet for retrieving items from a location (file system, registry, certificate store). It’s equivalent to dir or ls in other shells.
Key capabilities:
- List files and directories
- Search recursively through directory trees
- Filter by name, extension, attributes, and date
- Access hidden and system files
- Work with various PowerShell providers
# Basic usage
Get-ChildItem -Path "C:\Temp"
# Common aliases
gci "C:\Temp" # Short form
dir "C:\Temp" # Familiar to Windows users
ls "C:\Temp" # Familiar to Linux users
Common Parameters:
| Parameter | Purpose | Example |
|---|---|---|
-Path | Location to search | "C:\Logs" |
-Filter | Filename pattern | "*.log" |
-Include | Multiple patterns | "*.txt","*.log" |
-Exclude | Exclude patterns | "*.bak","*.tmp" |
-Recurse | Search subdirectories | (switch) |
-Directory | Directories only | (switch) |
-File | Files only | (switch) |
-Depth | Recursion limit | 2 |
For more PowerShell commands, see our Command Line Reference Hub.
Why Use Filtering? {#why-use-filtering}
Filtering at the cmdlet level (not in a pipeline) provides significant benefits:
Performance Improvement:
# Slow: Filter after retrieval (all items loaded first)
Get-ChildItem -Path "C:\Logs" | Where-Object {$_.Extension -eq ".log"}
# Fast: Filter at source (only matching items loaded)
Get-ChildItem -Path "C:\Logs" -Filter "*.log"
Performance difference: 50-70% faster with cmdlet-level filtering on large directories.
Benefits:
- Reduced memory usage (fewer items loaded)
- Faster execution (fewer objects to process)
- Cleaner, more readable code
- Better error handling
Filter vs Include vs Exclude {#filter-comparison}
Understanding which parameter to use is critical for optimal performance.
-Filter Parameter
Best for: Single file type/pattern with maximum performance
Characteristics:
- Fastest option (provider-level filtering)
- Single pattern only
- Uses wildcard (* and ?)
- Case-insensitive
# Get all .txt files
Get-ChildItem -Path "C:\Docs" -Filter "*.txt"
# Get files matching pattern
Get-ChildItem -Filter "report_*.log"
-Include Parameter
Best for: Multiple file types/patterns
Characteristics:
- Supports multiple patterns
- Slower than -Filter (post-retrieval filtering)
- Requires
-Pathwith\*wildcard OR-Recurse - Case-insensitive
# Multiple file types (REQUIRES wildcard in path)
Get-ChildItem -Path "C:\Docs\*" -Include "*.txt","*.docx","*.pdf"
# With recursion
Get-ChildItem -Path "C:\Logs" -Include "*.log","*.txt" -Recurse
-Exclude Parameter
Best for: Removing specific files/patterns
Characteristics:
- Works with -Filter or -Include
- Supports multiple patterns
- Post-retrieval filtering
# Exclude temporary files
Get-ChildItem -Path "C:\Data" -Exclude "*.tmp","*.bak"
# Combine with -Filter
Get-ChildItem -Path "C:\Logs" -Filter "*.log" -Exclude "debug*"
Comparison Table
| Feature | -Filter | -Include | -Exclude |
|---|---|---|---|
| Speed | ⚡ Fastest | 🟡 Medium | 🟡 Medium |
| Multiple Patterns | ❌ Single | ✅ Multiple | ✅ Multiple |
| Requires Wildcard | ❌ No | ✅ Yes* | ❌ No |
| Provider-Level | ✅ Yes | ❌ No | ❌ No |
*Requires -Path "C:\*" OR -Recurse
💡 Pro Tip: Use -Filter for single patterns (fastest). Use -Include only when you need multiple file types.
Filter by File Extension {#filter-by-extension}
One of the most common filtering tasks.
Get Files with Specific Extension
# Get all .txt files
Get-ChildItem -Path "C:\Documents" -Filter "*.txt"
# Get all .log files
Get-ChildItem -Path "C:\Logs" -Filter "*.log"
# Get PowerShell scripts
Get-ChildItem -Path "C:\Scripts" -Filter "*.ps1"
# Get all executables
Get-ChildItem -Path "C:\Windows\System32" -Filter "*.exe"
Get Multiple File Types
# Text, log, and config files
Get-ChildItem -Path "C:\Data\*" -Include "*.txt","*.log","*.config"
# Office documents
Get-ChildItem -Path "C:\Documents\*" -Include "*.docx","*.xlsx","*.pptx"
# Source code files
Get-ChildItem -Path "C:\Projects\*" -Include "*.cs","*.ps1","*.py" -Recurse
Get Files WITHOUT Extension
# Files with no extension
Get-ChildItem -Path "C:\Data" -File | Where-Object {$_.Extension -eq ""}
# Or use filter pattern
Get-ChildItem -Path "C:\Scripts" -Filter "*."
Filter by Filename Pattern {#filter-by-pattern}
Using wildcards to match file names.
Wildcard Characters
| Character | Meaning | Example | Matches |
|---|---|---|---|
* | Zero or more characters | *.txt | All .txt files |
? | Exactly one character | file?.txt | file1.txt, fileA.txt |
Examples
# Files starting with specific prefix
Get-ChildItem -Filter "backup_*"
# Files ending with specific suffix
Get-ChildItem -Filter "*_old.*"
# Single character wildcard
Get-ChildItem -Filter "log?.txt" # log1.txt, logA.txt, log_.txt
# Files containing pattern
Get-ChildItem -Filter "*error*"
# Date pattern files
Get-ChildItem -Filter "report_2024_*"
Real-World Patterns
# Log files from today
Get-ChildItem -Filter "*.log"
# Old backup files
Get-ChildItem -Filter "*_backup_*"
# Temporary files
Get-ChildItem -Filter "tmp*"
# Database files
Get-ChildItem -Filter "*.db*"
# Archive files
Get-ChildItem -Filter "*.zip", "*.rar", "*.7z"
Using -Include Parameter {#using-include}
For multiple file patterns with more flexibility.
Multiple File Types
# Method 1: Direct path with wildcard
Get-ChildItem -Path "C:\Data\*" -Include "*.txt","*.log","*.csv"
# Method 2: Current directory
Get-ChildItem -Include "*.ps1","*.bat","*.cmd"
# Method 3: With recursion
Get-ChildItem -Path "C:\App" -Include "*.config","*.json","*.xml" -Recurse
Include with Directory Recursion
# Find all configuration files
Get-ChildItem -Path "C:\App" -Include "*.config","*.settings" -Recurse
# Find all source code
Get-ChildItem -Path "C:\Projects" -Include "*.cs","*.vb","*.cpp" -Recurse
# Find all Office documents
Get-ChildItem -Path "C:\Users" -Include "*.docx","*.xlsx" -Recurse
Important: Must use wildcard in path (C:\Logs\*) OR include -Recurse
Using -Exclude Parameter {#using-exclude}
Remove unwanted files from results.
Exclude File Types
# Get all files except backups
Get-ChildItem -Path "C:\Data" -Exclude "*.bak","*.tmp","*.old"
# Exclude specific names
Get-ChildItem -Path "C:\Logs" -Exclude "debug.log","test_*.log"
# Exclude system folders
Get-ChildItem -Path "C:\Projects" -Exclude "bin","obj","node_modules"
Combine -Filter and -Exclude
# Get .log files except debug logs
Get-ChildItem -Path "C:\Logs" -Filter "*.log" -Exclude "debug*","test*"
# Get .txt files except README
Get-ChildItem -Filter "*.txt" -Exclude "README*"
Exclude Multiple Patterns
# Exclude multiple file types
Get-ChildItem -Exclude "*.tmp","*.bak","*.old","*.backup"
# Exclude system files
Get-ChildItem -Exclude ".git",".svn","packages","bin","obj"
Recursive Search {#recursive-search}
Search through all subdirectories with -Recurse.
Basic Recursive Search
# Find all .txt files in directory tree
Get-ChildItem -Path "C:\Documents" -Filter "*.txt" -Recurse
# Find all PowerShell scripts
Get-ChildItem -Path "C:\" -Filter "*.ps1" -Recurse
# Find all log files
Get-ChildItem -Path "C:\App" -Filter "*.log" -Recurse
Limit Recursion Depth
# Search only 2 levels deep (PowerShell 5.1+)
Get-ChildItem -Path "C:\Projects" -Filter "*.cs" -Recurse -Depth 2
# Search immediate subdirectories only
Get-ChildItem -Path "C:\Data" -Recurse -Depth 1
# Search very deep directory structure with limit
Get-ChildItem -Path "C:\" -Filter "*.exe" -Recurse -Depth 5
Files Only vs Directories Only {#files-directories}
Be explicit about what you’re searching for.
Get Only Files
# All files in directory
Get-ChildItem -Path "C:\Temp" -File
# Specific file type only
Get-ChildItem -Path "C:\Logs" -Filter "*.log" -File
# Recursive file search
Get-ChildItem -Path "C:\Data" -Filter "*.txt" -File -Recurse
Get Only Directories
# All directories
Get-ChildItem -Path "C:\Projects" -Directory
# Directories matching pattern
Get-ChildItem -Path "C:\Apps" -Directory -Filter "Backup*"
# Find all "bin" or "obj" folders recursively
Get-ChildItem -Path "C:\Projects" -Directory -Filter "bin" -Recurse
Count Files vs Directories
$path = "C:\Data"
# Count files
$fileCount = (Get-ChildItem -Path $path -File -Recurse | Measure-Object).Count
# Count directories
$dirCount = (Get-ChildItem -Path $path -Directory -Recurse | Measure-Object).Count
Write-Host "Files: $fileCount, Directories: $dirCount"
Hidden and System Files {#hidden-files}
Include hidden or system files in search results.
View Hidden Files
# Include hidden files
Get-ChildItem -Path "C:\Users" -Hidden
# Include hidden .txt files
Get-ChildItem -Path "C:\Temp" -Filter "*.txt" -Hidden
# All files including hidden (recursive)
Get-ChildItem -Path "C:\" -Hidden -Recurse
View System Files
# Include system files
Get-ChildItem -Path "C:\Windows" -System
# Use -Force to include both hidden and system
Get-ChildItem -Path "C:\" -Force
# Hidden and system .log files
Get-ChildItem -Filter "*.log" -Force
Real-World Use Cases {#real-world-use-cases}
Use Case 1: Clean Old Log Files
# Delete logs older than 30 days
$cutoffDate = (Get-Date).AddDays(-30)
Get-ChildItem -Path "C:\Logs" -Filter "*.log" -Recurse |
Where-Object { $_.LastWriteTime -lt $cutoffDate } |
Remove-Item -Force
Write-Host "Old log files deleted"
Use Case 2: Find Large Files
# Find files larger than 100MB
Get-ChildItem -Path "C:\Data" -File -Recurse |
Where-Object { $_.Length -gt 100MB } |
Select-Object FullName, @{Name="SizeMB";Expression={[math]::Round($_.Length/1MB,2)}} |
Sort-Object SizeMB -Descending |
Format-Table
Use Case 3: Inventory Executables
# Find all .exe and .dll files
$binaries = Get-ChildItem -Path "C:\Program Files" -Include "*.exe","*.dll" -Recurse
$report = $binaries | Select-Object Name, @{Name="SizeKB";Expression={[math]::Round($_.Length/1KB,2)}},
LastWriteTime
$report | Export-Csv -Path "C:\Reports\BinaryInventory.csv" -NoTypeInformation
Use Case 4: Find Files Modified Today
# Files modified in last 24 hours
$today = (Get-Date).Date
Get-ChildItem -Path "C:\Data" -File -Recurse |
Where-Object { $_.LastWriteTime -ge $today } |
Sort-Object LastWriteTime -Descending
Use Case 5: Generate Directory Report
# Get directory sizes
$dirs = Get-ChildItem -Path "C:\Data" -Directory
foreach ($dir in $dirs) {
$size = (Get-ChildItem -Path $dir.FullName -Recurse -File |
Measure-Object -Property Length -Sum).Sum
$sizeMB = [math]::Round($size / 1MB, 2)
Write-Host "$($dir.Name): ${sizeMB}MB"
}
Performance Optimization {#performance}
Speed Comparison
# Test 1: Using -Filter (FASTEST)
Measure-Command {
Get-ChildItem -Path "C:\Windows" -Filter "*.log" -Recurse | Measure-Object
}
# ~2,000ms
# Test 2: Using -Include (SLOWER)
Measure-Command {
Get-ChildItem -Path "C:\Windows\*" -Include "*.log" -Recurse | Measure-Object
}
# ~2,500ms
# Test 3: Using Where-Object (SLOWEST)
Measure-Command {
Get-ChildItem -Path "C:\Windows" -Recurse |
Where-Object {$_.Extension -eq ".log"} | Measure-Object
}
# ~3,500ms
Result: -Filter is ~40% faster than Where-Object pipeline filtering!
Performance Tips
# ✅ Best: Use -Filter for single pattern
Get-ChildItem -Path "C:\Logs" -Filter "*.log"
# ✅ Good: Limit recursion depth
Get-ChildItem -Path "C:\Data" -Filter "*.txt" -Recurse -Depth 3
# ✅ Good: Use -File to skip directories
Get-ChildItem -Path "C:\Logs" -Filter "*.log" -File -Recurse
# ❌ Avoid: Deep recursion without depth limit
Get-ChildItem -Path "C:\" -Filter "*.txt" -Recurse
# ❌ Avoid: Pipeline filtering on large datasets
Get-ChildItem | Where-Object { $_.Name -like "*.log" }
Troubleshooting {#troubleshooting}
Issue 1: -Include Not Working
Problem:
# This doesn't work!
Get-ChildItem -Path "C:\Logs" -Include "*.log"
# Returns: nothing
Solution: Add wildcard to path or use -Recurse
# Correct way 1: Wildcard in path
Get-ChildItem -Path "C:\Logs\*" -Include "*.log"
# Correct way 2: Use -Recurse
Get-ChildItem -Path "C:\Logs" -Include "*.log" -Recurse
Issue 2: Access Denied on Protected Directories
Problem: Errors when accessing system folders
Solution: Use -ErrorAction SilentlyContinue
Get-ChildItem -Path "C:\" -Filter "*.log" -Recurse -ErrorAction SilentlyContinue
Issue 3: Too Many Results
Problem: Search returns thousands of files
Solution: Limit depth or use more specific filter
# Limit recursion depth
Get-ChildItem -Path "C:\Projects" -Filter "*.cs" -Recurse -Depth 2
# More specific pattern
Get-ChildItem -Filter "report_2024_*"
Issue 4: Command Takes Forever
Problem: Searching entire C: drive is too slow
Solution: Start with specific directories and use -Depth
# Start narrow, not broad
Get-ChildItem -Path "C:\App" -Filter "*.log" -Recurse -Depth 3
# Avoid this
# Get-ChildItem -Path "C:\" -Filter "*" -Recurse
Common Mistakes {#common-mistakes}
❌ Mistake 1: Using -Include Without Wildcard
# WRONG
Get-ChildItem -Path "C:\Logs" -Include "*.log"
# CORRECT
Get-ChildItem -Path "C:\Logs\*" -Include "*.log"
# OR
Get-ChildItem -Path "C:\Logs" -Include "*.log" -Recurse
❌ Mistake 2: Using Pipeline Filter Instead of -Filter
# SLOW
Get-ChildItem -Path "C:\Logs" | Where-Object {$_.Name -like "*.log"}
# FAST
Get-ChildItem -Path "C:\Logs" -Filter "*.log"
❌ Mistake 3: Forgetting -File or -Directory
# Returns BOTH files and folders
Get-ChildItem -Filter "Backup*"
# Returns ONLY directories
Get-ChildItem -Filter "Backup*" -Directory
# Returns ONLY files
Get-ChildItem -Filter "Backup*" -File
❌ Mistake 4: Deep Recursion Without Depth Limit
# Very slow - searches entire drive
Get-ChildItem -Path "C:\" -Filter "*.txt" -Recurse
# Better - limited depth
Get-ChildItem -Path "C:\Users" -Filter "*.txt" -Recurse -Depth 3
❌ Mistake 5: Assuming Case-Sensitive Matching
# PowerShell filtering is case-INSENSITIVE
Get-ChildItem -Filter "*.LOG" # Matches .log, .LOG, .Log
# For case-sensitive, use Where-Object with -cmatch
Get-ChildItem | Where-Object { $_.Name -cmatch "\.LOG$" }
Best Practices {#best-practices}
✅ Use -Filter for Single Patterns
# Best performance
Get-ChildItem -Path "C:\Logs" -Filter "*.log"
✅ Use -Include for Multiple Patterns
# Multiple file types
Get-ChildItem -Path "C:\Data\*" -Include "*.txt","*.log","*.csv"
✅ Always Specify -File or -Directory
# Be explicit about what you want
Get-ChildItem -Path "C:\Data" -File
Get-ChildItem -Path "C:\Data" -Directory
✅ Limit Recursion Depth
# Prevent deep searches
Get-ChildItem -Path "C:\Data" -Recurse -Depth 3
✅ Handle Errors Gracefully
Get-ChildItem -Path "C:\Protected" -Recurse -ErrorAction SilentlyContinue
✅ Store Results for Reuse
# Store results if using multiple times
$logFiles = Get-ChildItem -Path "C:\Logs" -Filter "*.log"
$logFiles | ForEach-Object { # ... }
$logFiles | Measure-Object -Sum # Reuse
Frequently Asked Questions {#faqs}
Q: What’s the difference between -Filter and -Include?
A: -Filter is faster but single pattern only. -Include supports multiple patterns but requires wildcard in path or -Recurse.
# Single pattern (use -Filter)
Get-ChildItem -Filter "*.log"
# Multiple patterns (use -Include)
Get-ChildItem -Path "C:\*" -Include "*.log","*.txt"
Q: How do I search case-sensitively?
A: PowerShell filtering is case-insensitive by default. Use Where-Object with -cmatch for case-sensitive:
Get-ChildItem | Where-Object { $_.Name -cmatch "^LOG" }
Q: Can I use regular expressions?
A: Not with -Filter. Use Where-Object with -match:
Get-ChildItem | Where-Object { $_.Name -match "^log_\d{4}" }
Q: How do I find files modified in last 7 days?
A: Combine with Where-Object:
$since = (Get-Date).AddDays(-7)
Get-ChildItem -Path "C:\Data" -Recurse |
Where-Object { $_.LastWriteTime -gt $since }
Q: Can I search multiple directories?
A: Yes, iterate with ForEach:
$paths = "C:\Logs", "D:\Archive", "E:\Backup"
$paths | ForEach-Object {
Get-ChildItem -Path $_ -Filter "*.log" -Recurse
}
Q: How do I find only empty directories?
A: Check if directory has no children:
Get-ChildItem -Path "C:\Data" -Directory -Recurse |
Where-Object { (Get-ChildItem -Path $_.FullName).Count -eq 0 }
Q: What’s the fastest way to count files?
A: Use Measure-Object:
(Get-ChildItem -Path "C:\Logs" -Filter "*.log" -Recurse | Measure-Object).Count
Q: Can I use -Filter with multiple extensions?
A: No, but use -Include:
# Wrong
Get-ChildItem -Filter "*.txt,*.log"
# Correct
Get-ChildItem -Path "C:\*" -Include "*.txt","*.log"
Q: How do I find duplicate files?
A: Use Get-FileHash:
Get-ChildItem -Path "C:\Data" -File -Recurse |
Get-FileHash |
Group-Object -Property Hash |
Where-Object { $_.Count -gt 1 }
Q: Why is my search so slow?
A: Likely reasons:
- Searching entire C: drive without depth limit
- Using pipeline
Where-Objectinstead of-Filter - Accessing slow network drives
- Including too many large subdirectories
Solution: Start specific, use -Filter, add -Depth limit.
Conclusion
Mastering Get-ChildItem filtering is essential for PowerShell efficiency. Key takeaways:
✅ Use -Filter for single patterns (40% faster than Where-Object)
✅ Use -Include for multiple file types (requires wildcard or -Recurse)
✅ Use -Exclude to remove unwanted files
✅ Specify -File or -Directory to be explicit
✅ Limit recursion depth to prevent slow searches
✅ Handle errors with -ErrorAction SilentlyContinue
For more file management:
- Complete PowerShell Guide - Master PowerShell
- PowerShell Delete Files - Remove files efficiently
- PowerShell Get-Content - Read file contents
- PowerShell Arrays - Work with file collections
You can find more topics about PowerShell automation on the ActiveDirectoryTools home page.