Skip to main content

PowerShell Get-ChildItem with Filter: Complete Guide [2024]

6 min read
powershell get-childitem filter files folders file-management

!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? {#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:

ParameterPurposeExample
-PathLocation to search"C:\Logs"
-FilterFilename pattern"*.log"
-IncludeMultiple patterns"*.txt","*.log"
-ExcludeExclude patterns"*.bak","*.tmp"
-RecurseSearch subdirectories(switch)
-DirectoryDirectories only(switch)
-FileFiles only(switch)
-DepthRecursion limit2

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 -Path with \* 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

CharacterMeaningExampleMatches
*Zero or more characters*.txtAll .txt files
?Exactly one characterfile?.txtfile1.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"

Search through all subdirectories with -Recurse.

# 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-Object instead 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:

You can find more topics about PowerShell automation on the ActiveDirectoryTools home page.