Searching Files
Note
Master Get-ChildItem and Select-String to find files by name, properties, or content efficiently.
Overview
Searching for files is one of the most common PowerShell tasks. Whether you need to find files by name, extension, date, size, or content, PowerShell provides powerful tools that go far beyond the basic Windows search. Get-ChildItem searches by file properties, while Select-String searches file contents.
Basic Syntax
# Get-ChildItem (alias: gci, ls, dir)
Get-ChildItem -Path C:\Folder -Filter *.txt -Recurse
# Select-String (grep for PowerShell)
Select-String -Path C:\Folder\*.txt -Pattern "error"
Key Points
Get-ChildItemfinds files by properties (name, date, size, attributes)Select-Stringsearches inside file contents- Use
-Recurseto search subfolders - Use
-Filterfor better performance than-Include - Combine cmdlets with pipeline for powerful filtering
Finding Files by Name
Search by Extension
# All .txt files in current directory
Get-ChildItem -Filter *.txt
# All .log files including subfolders
Get-ChildItem -Path C:\Logs -Filter *.log -Recurse
# Multiple extensions
Get-ChildItem -Path C:\Temp -Include *.txt,*.log,*.csv -Recurse
Search by Name Pattern
# Files starting with "report"
Get-ChildItem -Path C:\Reports -Filter report*
# Files containing "backup" anywhere in name
Get-ChildItem -Path C:\Data -Filter *backup* -Recurse
# Exact file name
Get-ChildItem -Path C:\Scripts -Filter Install.ps1 -Recurse
Case-Sensitive Search
# Find files with exact case match
Get-ChildItem -Path C:\Temp | Where-Object {
$_.Name -clike "*ERROR*" # -clike is case-sensitive
}
Finding Files by Properties
Search by Date
# Files modified in last 7 days
$cutoffDate = (Get-Date).AddDays(-7)
Get-ChildItem -Path C:\Data -Recurse | Where-Object {
$_.LastWriteTime -gt $cutoffDate
}
# Files created today
Get-ChildItem -Path C:\Temp -Recurse | Where-Object {
$_.CreationTime.Date -eq (Get-Date).Date
}
# Files older than 30 days
$oldDate = (Get-Date).AddDays(-30)
Get-ChildItem -Path C:\Archive -Recurse | Where-Object {
$_.LastWriteTime -lt $oldDate
}
# Files modified between two dates
$startDate = Get-Date "2025-01-01"
$endDate = Get-Date "2025-12-31"
Get-ChildItem -Path C:\Reports -Recurse | Where-Object {
$_.LastWriteTime -ge $startDate -and $_.LastWriteTime -le $endDate
}
Search by Size
# Files larger than 100 MB
Get-ChildItem -Path C:\Downloads -Recurse | Where-Object {
$_.Length -gt 100MB
}
# Files between 1 MB and 10 MB
Get-ChildItem -Path C:\Data -Recurse | Where-Object {
$_.Length -ge 1MB -and $_.Length -le 10MB
}
# Empty files (0 bytes)
Get-ChildItem -Path C:\Temp -Recurse -File | Where-Object {
$_.Length -eq 0
}
# Show files sorted by size
Get-ChildItem -Path C:\Logs -Recurse -File |
Sort-Object Length -Descending |
Select-Object Name, @{Name="SizeMB";Expression={[math]::Round($_.Length/1MB,2)}}
Search by Attributes
# Hidden files
Get-ChildItem -Path C:\Windows -Hidden -Force
# Read-only files
Get-ChildItem -Path C:\Data -Recurse | Where-Object {
$_.IsReadOnly
}
# System files
Get-ChildItem -Path C:\Windows -System -Force
# Files that are NOT read-only
Get-ChildItem -Path C:\Temp -Recurse | Where-Object {
-not $_.IsReadOnly
}
# Archived files
Get-ChildItem -Path C:\Backup -Recurse | Where-Object {
$_.Attributes -match "Archive"
}
Searching File Contents
Basic Content Search
# Find files containing "error"
Select-String -Path C:\Logs\*.log -Pattern "error"
# Case-sensitive search
Select-String -Path C:\Logs\*.log -Pattern "ERROR" -CaseSensitive
# Search recursively
Select-String -Path C:\Logs\*.log -Pattern "error" -Recurse
# Show only file names (not matching lines)
Select-String -Path C:\Logs\*.log -Pattern "error" -List
Output:
C:\Logs\app.log:15:ERROR: Failed to connect
C:\Logs\app.log:42:ERROR: Timeout exceeded
C:\Logs\system.log:8:Error occurred during startup
Advanced Content Search
# Multiple patterns (OR logic)
Select-String -Path C:\Logs\*.log -Pattern "error","warning","critical"
# Get context lines (before and after match)
Select-String -Path C:\Logs\app.log -Pattern "error" -Context 2,3
# Shows 2 lines before and 3 lines after each match
# Search specific file types
Get-ChildItem -Path C:\Code -Include *.ps1,*.psm1 -Recurse |
Select-String -Pattern "function Get-"
# Count matches per file
Get-ChildItem -Path C:\Logs -Filter *.log | ForEach-Object {
$matches = Select-String -Path $_.FullName -Pattern "error" -AllMatches
[PSCustomObject]@{
File = $_.Name
ErrorCount = $matches.Count
}
}
Regular Expression Search
# Find IP addresses in files
Select-String -Path C:\Logs\*.log -Pattern "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"
# Find email addresses
Select-String -Path C:\Data\*.txt -Pattern "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
# Find dates in YYYY-MM-DD format
Select-String -Path C:\Reports\*.txt -Pattern "\d{4}-\d{2}-\d{2}"
# Find lines starting with "ERROR:"
Select-String -Path C:\Logs\*.log -Pattern "^ERROR:"
# Find lines ending with "failed"
Select-String -Path C:\Logs\*.log -Pattern "failed$"
Excluding Folders and Files
Skip Specific Folders
# Exclude system folders
Get-ChildItem -Path C:\Code -Recurse | Where-Object {
$_.FullName -notmatch "\\node_modules\\|\\\.git\\|\\bin\\|\\obj\\"
}
# Exclude multiple folder patterns
Get-ChildItem -Path C:\Projects -Recurse -File | Where-Object {
$_.DirectoryName -notlike "*\node_modules\*" -and
$_.DirectoryName -notlike "*\.git\*" -and
$_.DirectoryName -notlike "*\bin\*"
}
Skip Specific File Types
# All files except .tmp and .bak
Get-ChildItem -Path C:\Data -Recurse | Where-Object {
$_.Extension -notin @(".tmp", ".bak")
}
# Exclude files matching pattern
Get-ChildItem -Path C:\Logs -Recurse | Where-Object {
$_.Name -notlike "*.old" -and $_.Name -notlike "*.backup"
}
Real-World Examples
Example: Find Large Old Log Files
Scenario: Clean up log files larger than 50MB that are older than 30 days
$path = "C:\Logs"
$sizeLimitMB = 50
$daysOld = 30
$cutoffDate = (Get-Date).AddDays(-$daysOld)
$oldLargeLogs = Get-ChildItem -Path $path -Filter *.log -Recurse | Where-Object {
$_.Length -gt ($sizeLimitMB * 1MB) -and
$_.LastWriteTime -lt $cutoffDate
}
# Display findings
$oldLargeLogs | Select-Object Name,
@{Name="SizeMB";Expression={[math]::Round($_.Length/1MB,2)}},
LastWriteTime,
FullName | Format-Table -AutoSize
# Optional: Delete after review
# $oldLargeLogs | Remove-Item -Force
Example: Search Code for Deprecated Functions
Scenario: Find all PowerShell scripts using a deprecated function
$searchPath = "C:\Scripts"
$deprecatedFunction = "Get-WmiObject"
$replacement = "Get-CimInstance"
$results = Get-ChildItem -Path $searchPath -Include *.ps1,*.psm1 -Recurse |
Select-String -Pattern $deprecatedFunction
# Display results grouped by file
$results | Group-Object Path | ForEach-Object {
Write-Output "`nFile: $($_.Name)"
Write-Output "Occurrences: $($_.Count)"
$_.Group | ForEach-Object {
Write-Output " Line $($_.LineNumber): $($_.Line.Trim())"
}
}
Write-Output "`nRecommendation: Replace '$deprecatedFunction' with '$replacement'"
Example: Find Duplicate Files by Name
Scenario: Identify files with the same name in different folders
$searchPath = "C:\Documents"
$duplicates = Get-ChildItem -Path $searchPath -Recurse -File |
Group-Object Name |
Where-Object {$_.Count -gt 1}
foreach ($dup in $duplicates) {
Write-Output "`nDuplicate file: $($dup.Name)"
Write-Output "Found in $($dup.Count) locations:"
$dup.Group | ForEach-Object {
Write-Output " - $($_.FullName) (Modified: $($_.LastWriteTime))"
}
}
Example: Search for Files Modified by Specific User
Scenario: Find files recently modified (requires NTFS and audit logging)
$targetUser = "DOMAIN\jsmith"
$searchPath = "C:\SharedData"
$daysBack = 7
$files = Get-ChildItem -Path $searchPath -Recurse -File | Where-Object {
$_.LastWriteTime -gt (Get-Date).AddDays(-$daysBack)
}
foreach ($file in $files) {
$acl = Get-Acl -Path $file.FullName
# Check owner or audit logs for modification info
if ($acl.Owner -eq $targetUser) {
[PSCustomObject]@{
FileName = $file.Name
Path = $file.DirectoryName
Modified = $file.LastWriteTime
Owner = $acl.Owner
}
}
}
Example: Generate File Inventory Report
Scenario: Create detailed inventory of all files in a directory
$path = "C:\ImportantData"
$reportPath = "C:\Reports\FileInventory_$(Get-Date -Format 'yyyyMMdd').csv"
Get-ChildItem -Path $path -Recurse -File | Select-Object `
Name,
Directory,
Extension,
@{Name="SizeMB";Expression={[math]::Round($_.Length/1MB,2)}},
CreationTime,
LastWriteTime,
LastAccessTime,
IsReadOnly,
@{Name="Age_Days";Expression={(New-TimeSpan -Start $_.CreationTime -End (Get-Date)).Days}} |
Export-Csv -Path $reportPath -NoTypeInformation
Write-Output "Inventory report saved to: $reportPath"
Performance Tips
Use -Filter Instead of -Include
Limit Depth When Possible
Use -File and -Directory Switches
Filter Early in Pipeline
Common Patterns
# Pattern 1: Find files and show size summary
Get-ChildItem -Path C:\Data -Recurse -File |
Measure-Object -Property Length -Sum |
Select-Object @{Name="TotalSizeGB";Expression={[math]::Round($_.Sum/1GB,2)}}
# Pattern 2: Group files by extension
Get-ChildItem -Path C:\Temp -Recurse -File |
Group-Object Extension |
Select-Object Name, Count |
Sort-Object Count -Descending
# Pattern 3: Find newest files
Get-ChildItem -Path C:\Downloads -File |
Sort-Object LastWriteTime -Descending |
Select-Object -First 10
# Pattern 4: Find files not accessed recently
$unusedDays = 180
Get-ChildItem -Path C:\Archive -Recurse -File | Where-Object {
$_.LastAccessTime -lt (Get-Date).AddDays(-$unusedDays)
}
# Pattern 5: Search and copy results
Get-ChildItem -Path C:\Source -Filter *.config -Recurse |
Copy-Item -Destination C:\Backup -Force
# Pattern 6: Find and replace in file contents
Get-ChildItem -Path C:\Scripts -Filter *.ps1 -Recurse | ForEach-Object {
(Get-Content $_.FullName) -replace 'oldtext','newtext' |
Set-Content $_.FullName
}
Tips & Tricks
Use Aliases for Speed
Save Search Results for Reuse
# Search once, use many times
$logFiles = Get-ChildItem -Path C:\Logs -Filter *.log -Recurse
# Now you can filter without re-searching
$largeFiles = $logFiles | Where-Object {$_.Length -gt 50MB}
$recentFiles = $logFiles | Where-Object {$_.LastWriteTime -gt (Get-Date).AddDays(-7)}
$oldFiles = $logFiles | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)}
Combine Select-String with Get-ChildItem
-Recurse on Large Drives
Hidden and System Files
Watch Out for Access Denied Errors
# This will show errors for protected folders
Get-ChildItem -Path C:\ -Recurse
# Suppress errors (be careful!)
Get-ChildItem -Path C:\ -Recurse -ErrorAction SilentlyContinue
# Better: Catch and log errors
Get-ChildItem -Path C:\ -Recurse -ErrorAction SilentlyContinue -ErrorVariable accessErrors
$accessErrors | Out-File C:\Logs\access_errors.log
Common Mistakes
Forgetting Wildcards in -Path
# BAD - This searches for a file literally named "*.txt"
Get-ChildItem -Path "C:\Logs\*.txt"
# Might work, but not if there's no wildcard support in that context
# GOOD - Use -Filter for wildcards (faster)
Get-ChildItem -Path "C:\Logs" -Filter "*.txt"
# OR use -Include with -Recurse
Get-ChildItem -Path "C:\Logs" -Include "*.txt" -Recurse
Using -Include Without -Recurse
# BAD - -Include does nothing without -Recurse!
Get-ChildItem -Path "C:\Logs" -Include "*.log"
# Returns ALL files, ignores -Include
# GOOD - Add -Recurse to make -Include work
Get-ChildItem -Path "C:\Logs" -Include "*.log" -Recurse
# OR use -Filter (works without -Recurse)
Get-ChildItem -Path "C:\Logs" -Filter "*.log"
Not Escaping Special Regex Characters in Select-String
# BAD - Looking for literal "error." but . matches any character
Select-String -Path "C:\Logs\*.log" -Pattern "error."
# Matches "error!", "errorX", etc.
# GOOD - Escape the period
Select-String -Path "C:\Logs\*.log" -Pattern "error\."
# OR use -SimpleMatch for literal strings
Select-String -Path "C:\Logs\*.log" -Pattern "error." -SimpleMatch
Searching Large Directories Without Limiting Scope
# BAD - Searches ENTIRE C: drive (very slow!)
Get-ChildItem -Path C:\ -Recurse -Filter "config.json"
# GOOD - Be specific about where to look
Get-ChildItem -Path "C:\Program Files\MyApp" -Recurse -Filter "config.json"
# BETTER - Use -Depth to limit how deep
Get-ChildItem -Path C:\Data -Recurse -Depth 2 -Filter "config.json"
Related Topics
- String Manipulation - Processing file names and content
- The Pipeline - Chaining search operations
Additional Resources
- Microsoft Docs: Get-ChildItem
- Microsoft Docs: Select-String
- PowerShell Gallery: PSScriptAnalyzer - For searching PowerShell code