PowerShell Search for Text in Files with Select-String

PowerShell Search for Text in Files with Select-String

PowerShell Tips Editor 3 min read
PowerShell Search for Text in Files with Select-String

A customer reports an error. The log folder has 200 files. You need to find every occurrence of that error across all of them, with line numbers, in under five seconds. Select-String is exactly the right tool — it’s PowerShell’s equivalent of grep, but it returns structured match objects instead of text lines, which means you can filter, sort, and export the results. This guide covers how to PowerShell search text in files from a single file to recursive multi-folder scans.

Quick Answer / TL;DR

# Search a single file for a pattern
Select-String -Path 'C:\Logs\app.log' -Pattern 'ERROR'

# Search all .log files recursively
Get-ChildItem 'C:\Logs' -Filter '*.log' -Recurse |
    Select-String -Pattern 'ERROR'

Search a Single File

Select-String returns MatchInfo objects with file name, line number, and the matching line:

# Basic search
Select-String -Path 'C:\Logs\app.log' -Pattern 'ERROR'

# Show just the essential fields
Select-String -Path 'C:\Logs\app.log' -Pattern 'ERROR' |
    Select-Object Filename, LineNumber, Line

# Count matches
(Select-String -Path 'C:\Logs\app.log' -Pattern 'ERROR').Count
C:\Logs\app.log:15:2026-05-04 08:15:33 ERROR Database connection failed
C:\Logs\app.log:47:2026-05-04 09:01:12 ERROR Timeout on request ID 8821
C:\Logs\app.log:112:2026-05-04 09:55:44 ERROR Null reference in ProcessOrder()

The output format is filename:linenumber:content when displayed. As objects, they have .Filename, .LineNumber, .Line, .Matches, and other properties.

Search Multiple Files with Wildcards

Pass a wildcard path to search multiple files at once:

# All .log files in a directory
Select-String -Path 'C:\Logs\*.log' -Pattern 'CRITICAL'

# Multiple file types
Select-String -Path 'C:\Config\*.json', 'C:\Config\*.xml' -Pattern 'password'

# Pipe Get-ChildItem for more control
Get-ChildItem 'C:\Logs' -Filter '*.log' |
    Select-String -Pattern 'Connection refused'
C:\Logs\app.log:88:2026-05-04 10:14:22 CRITICAL Service unreachable
C:\Logs\system.log:201:2026-05-04 10:14:30 CRITICAL Node failover triggered

Recursive Search Across Folders

Combine Get-ChildItem -Recurse with Select-String to search an entire directory tree:

# Search all .log files in all subfolders
Get-ChildItem 'C:\Logs' -Filter '*.log' -Recurse |
    Select-String -Pattern 'ORA-\d+'   # Oracle error codes

# Search all PowerShell scripts for a function call
Get-ChildItem 'C:\Scripts' -Filter '*.ps1' -Recurse |
    Select-String -Pattern 'Send-MailMessage' |
    Select-Object Filename, LineNumber, Line
Filename           LineNumber Line
--------           ---------- ----
deploy.ps1         45         Send-MailMessage -To '[email protected]' ...
weekly-report.ps1  112        Send-MailMessage -SmtpServer 'mail.corp' ...

Case-Insensitive Matching

By default, Select-String is case-insensitive. To force case sensitivity:

# Default: case-insensitive
Select-String -Path 'C:\Logs\*.log' -Pattern 'error'    # Matches ERROR, Error, error

# Case-sensitive with -CaseSensitive
Select-String -Path 'C:\Logs\*.log' -Pattern 'ERROR' -CaseSensitive   # Only uppercase ERROR

# Find lines that do NOT match (inverted search)
Select-String -Path 'C:\Logs\app.log' -Pattern 'ERROR' -NotMatch

Show Context Lines with -Context

See lines before and after each match for context — invaluable for log analysis:

# Show 2 lines before and 2 lines after each match
Select-String -Path 'C:\Logs\app.log' -Pattern 'ERROR' -Context 2, 2

# Show 5 lines before only (useful for finding what caused the error)
Select-String -Path 'C:\Logs\app.log' -Pattern 'FATAL' -Context 5, 0

# Format context output clearly
Select-String 'C:\Logs\app.log' -Pattern 'ERROR' -Context 1, 0 |
    ForEach-Object {
        Write-Output "--- Match in $($_.Filename) line $($_.LineNumber) ---"
        $_.Context.PreContext | ForEach-Object { "  PRE: $_" }
        "  MATCH: $($_.Line)"
    }
--- Match in app.log line 15 ---
  PRE: 2026-05-04 08:15:30 INFO Connecting to database
  MATCH: 2026-05-04 08:15:33 ERROR Database connection failed

Export Matches to CSV

Get-ChildItem 'C:\Logs' -Filter '*.log' -Recurse |
    Select-String -Pattern 'ERROR|CRITICAL|FATAL' |
    Select-Object Filename, LineNumber, Line,
        @{ Name='Severity'; Expression={
            if ($_.Line -match 'FATAL') { 'FATAL' }
            elseif ($_.Line -match 'CRITICAL') { 'CRITICAL' }
            else { 'ERROR' }
        } } |
    Sort-Object Severity, Filename, LineNumber |
    Export-Csv 'C:\Reports\log-errors.csv' -NoTypeInformation

Common Errors and Fixes

  • Pattern is regex — escape dots and brackets for literal search: Select-String -Pattern '192.168.1.1' matches 192X168Y1Z1 because . is a regex wildcard. For literal text, use -SimpleMatch flag or escape with backslash: '192\.168\.1\.1'.
  • Binary files cause encoding errors: Running Select-String against .dll, .exe, or image files throws encoding errors. Add -ErrorAction SilentlyContinue or filter file types with -Include '*.log','*.txt' to avoid binary files.

Related Cmdlets / See Also

Wrapping Up

Select-String is the most powerful text search tool in PowerShell. Use wildcards and Get-ChildItem -Recurse for multi-file searches, -Context to see surrounding lines, and export to CSV for incident reports. Remember to use -SimpleMatch or escape special characters when searching for literal text. Your next step: set up a weekly error report script that scans your log folder and emails a CSV of all CRITICAL matches.

Send-Item -To