PowerShell Select-String

File and stream text search with Select-String — the PowerShell grep equivalent.

Select-String Quick Reference (PowerShell grep)

Basic pattern search
Select-String -Path *.log -Pattern "ERROR"
Select-String -Path *.log -Pattern "ERROR" -CaseSensitive
Recursive search
Get-ChildItem -Recurse -Filter *.adoc | Select-String -Pattern "hardcoded"
Context lines (like grep -A/-B/-C)
Select-String -Path auth.log -Pattern "Failed" -Context 2,3
# 2 lines before, 3 lines after
Extract matched value only
Get-Content log.txt | Select-String -Pattern '\d+\.\d+\.\d+\.\d+' -AllMatches |
    ForEach-Object { $_.Matches.Value } | Sort-Object -Unique
Multiple patterns
Select-String -Path *.log -Pattern "ERROR", "FATAL", "CRITICAL"
Invert match (like grep -v)
Select-String -Path config.txt -Pattern "^#" -NotMatch
# Lines NOT starting with #
Search with file filtering
Get-ChildItem -Recurse -Include *.ps1, *.psm1 |
    Select-String -Pattern "Invoke-Command" |
    Select-Object Path, LineNumber, Line
Pipe to object manipulation (the PowerShell advantage over grep)
Select-String -Path *.log -Pattern "Failed login from (?<ip>\d+\.\d+\.\d+\.\d+)" |
    ForEach-Object { $_.Matches.Groups['ip'].Value } |
    Group-Object | Sort-Object Count -Descending | Select-Object -First 10
# Top 10 source IPs for failed logins — structured, not text

Select-String (PowerShell grep)

Search a file
Select-String -Path *.log -Pattern "ERROR"
Select-String -Path *.log -Pattern "ERROR" -CaseSensitive
Recursive search (like grep -r)
Get-ChildItem -Recurse -Filter *.adoc | Select-String -Pattern "hardcoded"
Shorthand (sls alias)
sls "Failed" auth.log
gci -r -fi *.log | sls "ERROR"

Context & Surrounding Lines

Context lines (like grep -C/-B/-A)
Select-String -Path auth.log -Pattern "Failed" -Context 2,3
# 2 lines before, 3 lines after
Only before (like grep -B)
Select-String -Path config.txt -Pattern "error" -Context 5,0

Pattern Extraction

Extract IP addresses from logs
Get-Content log.txt | Select-String -Pattern '\d+\.\d+\.\d+\.\d+' -AllMatches |
    ForEach-Object { $_.Matches.Value } | Sort-Object -Unique
Named capture groups — extract structured data
Select-String -Path *.log -Pattern "Failed login from (?<ip>\d+\.\d+\.\d+\.\d+) user (?<user>\w+)" |
    ForEach-Object {
        [PSCustomObject]@{
            IP   = $_.Matches.Groups['ip'].Value
            User = $_.Matches.Groups['user'].Value
            File = $_.Filename
            Line = $_.LineNumber
        }
    } | Format-Table
Count matches per file (like grep -c)
Get-ChildItem -Recurse -Filter *.log | ForEach-Object {
    $count = (Select-String -Path $_.FullName -Pattern "ERROR" -SimpleMatch).Count
    if ($count -gt 0) { [PSCustomObject]@{File=$_.Name; Errors=$count} }
} | Sort-Object Errors -Descending

Invert & Filter

Invert match (like grep -v)
Select-String -Path config.txt -Pattern "^#" -NotMatch
Multiple patterns (like grep -E "a|b|c")
Select-String -Path *.log -Pattern "ERROR|WARN|CRITICAL"
Exclude patterns — show errors but not expected ones
Select-String -Path app.log -Pattern "ERROR" |
    Where-Object { $_.Line -notmatch "expected|timeout|retry" }

Pipeline Power (what bash grep can’t do)

Top 10 failed IPs — structured group-by
Select-String -Path auth.log -Pattern "Failed.*from (?<ip>\d+\.\d+\.\d+\.\d+)" |
    ForEach-Object { $_.Matches.Groups['ip'].Value } |
    Group-Object | Sort-Object Count -Descending |
    Select-Object -First 10 Name, Count
Search + transform + export to CSV
Get-ChildItem -Recurse -Filter *.log |
    Select-String -Pattern "(?<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*ERROR.*(?<msg>.*)" |
    ForEach-Object {
        [PSCustomObject]@{
            Timestamp = $_.Matches.Groups['ts'].Value
            Message   = $_.Matches.Groups['msg'].Value
            File      = $_.Filename
        }
    } | Export-Csv -Path errors.csv -NoTypeInformation
Real-time log monitoring (like tail -f | grep)
Get-Content -Path app.log -Wait -Tail 0 | Select-String -Pattern "ERROR|CRITICAL"

Network Forensics

Parse Windows event log for auth failures
Get-WinEvent -LogName Security -MaxEvents 1000 |
    Where-Object { $_.Id -eq 4625 } |
    ForEach-Object { $_.Message } |
    Select-String -Pattern "Account Name:\s+(\S+)" -AllMatches |
    ForEach-Object { $_.Matches.Groups[1].Value } |
    Group-Object | Sort-Object Count -Descending | Select-Object -First 10
Find all IPs in any text file
Get-Content output.txt |
    Select-String -Pattern '\b(?:\d{1,3}\.){3}\d{1,3}\b' -AllMatches |
    ForEach-Object { $_.Matches.Value } | Sort-Object { [version]$_ } -Unique
Search running config for security issues
# Search a switch config dump for common issues
$config = Get-Content switch-config.txt
$config | Select-String -Pattern "permit any any|no service password|telnet|enable password"

Comparison: bash grep vs PowerShell

Task bash PowerShell

Basic search

grep "ERROR" file.log

sls "ERROR" file.log

Recursive

grep -r "TODO" --include="*.py" .

gci -r -fi *.py | sls "TODO"

Count

grep -c "ERROR" file.log

(sls "ERROR" file.log).Count

Extract IPs

grep -oP '\d+\.\d+\.\d+\.\d+' file

sls '\d+\.\d+\.\d+\.\d+' file -A | % { $_.Matches.Value }

Context

grep -C3 "error" file

sls "error" file -Context 3,3

Invert

grep -v "^#" file

sls "^#" file -NotMatch

Top talkers

grep -oP 'from \K[\d.]+' auth.log | sort | uniq -c | sort -rn

sls 'from (?<ip>[\d.]+)' auth.log | % { $_.Matches.Groups['ip'].Value } | Group | Sort Count -Desc

Files only

grep -rl "pattern" .

gci -r | sls "pattern" | Select-Object -Unique Path

Real-time

tail -f log | grep ERROR

gc log -Wait -Tail 0 | sls ERROR

The PowerShell advantage: every match is an object with .Filename, .LineNumber, .Line, .Matches, .Matches.Groups. You can pipeline into Group-Object, Sort-Object, Export-Csv, ConvertTo-Json — no awk required.

See Also

  • Regex — regex operator syntax and .NET patterns

  • Pipelines — piping output into Select-String