cheat sheet

findstr

Search for string patterns inside files or piped input on Windows — the built-in grep equivalent for cmd.exe, with support for regular expressions, case-insensitive matching, and recursive directory searches.

findstr — Search Text in Files

What it is

findstr is a built-in Windows command that searches one or more files — or piped input — for lines containing a given string or regular expression pattern. It is the native cmd.exe equivalent of Unix grep: use it to scan log files, filter command output, check configuration files, and locate text across directory trees. For more powerful regex, PowerShell's Select-String is the modern alternative.

Availability

findstr ships as C:\Windows\System32\findstr.exe on Windows XP and later.

cmd
findstr /?

Output:

css
Searches for strings in files.

FINDSTR [/B] [/E] [/L] [/R] [/S] [/I] [/X] [/V] [/N] [/M] [/O] [/F:file]
        [/C:string] [/G:file] [/D:dir list] [/A:color attributes] [/OFF[LINE]]
        strings [[drive:][path]filename[ ...]]

Syntax

cmd
findstr [options] "pattern" [file ...]

Output: (matching lines, one per line)

Essential options

SwitchMeaning
/ICase-insensitive match
/RTreat pattern as a regular expression
/SSearch subdirectories recursively
/NPrefix matching lines with line number
/MPrint only filenames that contain a match
/VInvert match — print lines that do NOT match
/LLiteral string match (default when not /R)
/BMatch at the beginning of a line
/EMatch at the end of a line
/XMatch the entire line exactly
/C:stringExplicit search string (allows spaces without quotes)
/G:fileRead search strings from a file, one per line
/F:fileRead file list from a file
/OPrint character offset before matching line

findstr without flags searches for the literal string and prints each matching line. The search is case-sensitive by default.

cmd
findstr "error" app.log

Output:

vbnet
2026-04-28 09:12:33 ERROR Connection refused on port 5432
2026-04-28 09:15:00 ERROR Retry limit exceeded
cmd
rem Case-insensitive search
findstr /I "error" app.log

Output:

yaml
2026-04-28 09:12:33 ERROR Connection refused on port 5432
2026-04-28 09:15:00 error: null pointer dereference

Filtering piped output

findstr commonly appears at the end of a pipeline to filter another command's output — the cmd.exe equivalent of piping to grep.

cmd
netstat -an | findstr ":8080"

Output:

code
  TCP    0.0.0.0:8080           0.0.0.0:0              LISTENING
cmd
tasklist | findstr /I "chrome"

Output:

arduino
chrome.exe                    6124 Console                    1    512,340 K
chrome.exe                    6130 Console                    1     98,456 K

Recursive directory search (/S)

/S walks the directory tree starting from the current directory (or a specified path) and searches every matching file.

cmd
findstr /S /I "TODO" *.txt

Output:

makefile
notes\project.txt:4:TODO: fix the auth bug
docs\readme.txt:22:TODO: add installation section
cmd
rem Show only filenames, not matching lines
findstr /S /M /I "password" *.config

Output:

arduino
config\app.config
config\database.config

Line numbers (/N) and offsets (/O)

/N prefixes each matching line with its 1-based line number. /O prefixes with the character offset from the beginning of the file — useful for pinpointing a match inside a large binary-adjacent text file.

cmd
findstr /N "FAILED" build.log

Output:

vbnet
47:FAILED: compile step returned exit code 1
103:FAILED: link step — missing symbol

Regular expressions (/R)

/R enables a limited regex dialect. Supported metacharacters: . (any char), * (zero or more of preceding), ^ (start of line), $ (end of line), [abc] (character class), [^abc] (negated class), \< (word start), \> (word end).

cmd
rem Lines starting with a 4-digit year
findstr /R "^[0-9][0-9][0-9][0-9]-" app.log

Output:

yaml
2026-04-28 09:12:33 ERROR Connection refused
2026-04-28 09:15:00 INFO  Service started
cmd
rem Match whole word "error" (not "errors")
findstr /R "\<error\>" app.log

Output:

yaml
2026-04-28 09:15:00 error: null pointer dereference

Multiple patterns

Search for multiple literal strings in a single pass by separating patterns with a space (when the pattern list is the non-/C: form) or by using /G:file.

cmd
rem Two patterns — lines matching either "error" OR "warn"
findstr /I "error warn" app.log

Output:

yaml
2026-04-28 09:12:33 ERROR Connection refused
2026-04-28 09:14:00 WARN  Retry 1 of 3
2026-04-28 09:15:00 ERROR Retry limit exceeded
cmd
rem Patterns from a file (one per line)
findstr /G:patterns.txt app.log

Output:

sql
(lines matching any pattern in patterns.txt)

Invert match (/V)

/V prints lines that do not contain the pattern — useful for filtering out noise lines such as debug output or comment lines.

cmd
rem Skip blank lines and REM comment lines
findstr /V /R "^$\|^rem" script.bat

Output:

ini
@echo off
set PATH=%PATH%;C:\Tools
call build.bat

Common pitfalls

  1. Space-separated patterns are OR, not ANDfindstr "foo bar" matches lines containing foo OR bar, not both; pipe two findstr calls to AND: findstr "foo" file | findstr "bar".
  2. Regex support is very limited — no +, ?, {n,m}, or lookaheads; use PowerShell Select-String for full PCRE-like patterns.
  3. Case-sensitive by default — always add /I unless you need exact case, or you'll miss mixed-case occurrences.
  4. /S searches from the current directory — if no path is specified, the working directory is the root of the recursive search; cd to the right folder first or specify a path.
  5. Wildcard *.log requires at least one file matchfindstr /S "text" *.log fails silently if no .log files exist in the tree; pair with if errorlevel 1 to detect.
  6. Quotes around pattern are required when pattern contains spacesfindstr connection refused file.log is parsed as two separate patterns; use /C:"connection refused" for a literal phrase.

Real-world recipes

Find which config file sets a specific key

cmd
findstr /S /M /I "ConnectionString" C:\App\*.config

Output:

arduino
C:\App\config\database.config
C:\App\config\legacy.config

Search log files for errors in the last hour (combined with more)

cmd
findstr /N /I "error\|exception\|fatal" C:\Logs\app.log | more

Output:

makefile
47:ERROR Connection refused
102:EXCEPTION NullPointerException in handler
(-- More --)

Extract lines containing an IP address pattern

cmd
findstr /R "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" access.log

Output:

bash
192.168.1.100 GET /index.html 200
10.0.0.5 POST /api/login 401

Confirm a process is running before killing it

cmd
@echo off
tasklist | findstr /I "myapp.exe" >NUL
if errorlevel 1 (
    echo myapp.exe is not running.
) else (
    taskkill /F /IM myapp.exe
    echo myapp.exe terminated.
)

Output:

code
myapp.exe terminated.

Complete switch reference

findstr exposes more switches than the essentials table covers. The full list:

SwitchMeaning
/BAnchor pattern at the beginning of a line
/EAnchor pattern at the end of a line
/LLiteral string search (default for non-/R patterns)
/RTreat each pattern as a regular expression
/SSearch all matching files in subdirectories
/ICase-insensitive match
/XMatch entire lines exactly
/VInvert — print lines that do NOT match
/NPrefix each line with its line number
/MPrint only filenames containing a match
/OPrefix each line with its character offset from start of file
/PSkip files containing non-printable characters
/F:fileRead list of files to search from file
/C:stringUse string as a single literal search term (allows spaces, treats as one)
/G:fileRead search patterns from file, one per line
/D:dir1;dir2Search this semicolon-delimited list of directories
/A:attrColour attribute for matched output (two hex digits — FG and BG)
/OFF[LINE]Do not skip files marked with the Offline attribute
/?Help

The /A colour attribute is two hex digits: background nibble then foreground nibble. /A:4F means bright white text on red background.

cmd
rem Coloured output — red background, white text on matches
findstr /N /A:4F "ERROR" app.log

Output: (matches displayed with red highlight)

Regex dialect — what findstr supports

findstr /R supports a strict subset of POSIX-BRE-like regex. Compared to grep, several quantifiers and shorthand classes are missing.

MetacharacterMeaningNotes
.Any single character
*Zero or more of previous (greedy)
^Start of line
$End of line
[abc]Any of a, b, c
[^abc]None of a, b, c
[a-z]Range
\<Word startGNU-style, not POSIX
\>Word end
\xHHHex character literalundocumented; works in modern Windows

Notable absences from findstr regex:

  • + (one or more) — use xx* instead (one literal then *).
  • ? (zero or one) — no replacement; rewrite as alternation, or use literal mode.
  • {n,m} (counted repetition) — no replacement.
  • | (alternation) — handled separately by space-separated patterns at the command-line level.
  • () (grouping) — not supported; effectively no captures.
  • \d, \w, \s shorthand classes — use [0-9], [A-Za-z0-9_], [ \t].
  • Look-ahead / look-behind — not supported.
cmd
rem One-or-more digits in findstr regex (no + — use xx*)
findstr /R "[0-9][0-9]*" data.txt

Output: (lines containing one or more digits)

cmd
rem Match an IPv4 address (no shorthand; long but works)
findstr /R "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" access.log

Output: (lines containing IP-like patterns)

cmd
rem Whole word boundaries with \< and \>
findstr /R "\<TODO\>" *.py

Output:

yaml
auth.py:42:    # TODO: rate limit
api.py:118:    # TODO: validate input

Pattern precedence and the /C: trap

When a pattern argument contains spaces, findstr treats it as multiple space-separated patterns by default — each is matched as a literal substring with OR semantics. This is the source of nearly every "why doesn't this match?" complaint about findstr.

cmd
rem WRONG — searches for either "connection" OR "refused"
findstr "connection refused" app.log

Output (matches both words separately):

yaml
2026-05-22 connection established
2026-05-22 client refused service
2026-05-22 connection refused on port 5432
cmd
rem RIGHT — /C: tells findstr to treat the argument as one literal string
findstr /C:"connection refused" app.log

Output:

yaml
2026-05-22 connection refused on port 5432
cmd
rem Multiple /C: phrases — OR of complete phrases
findstr /C:"connection refused" /C:"timeout exceeded" /C:"out of memory" app.log

Output: (lines matching any of the three phrases)

For complex pattern sets, /G:file reads patterns from a file — one pattern per line. This avoids quoting issues entirely:

cmd
rem patterns.txt
connection refused
timeout exceeded
out of memory

Output: (none — this is the contents of patterns.txt)

cmd
findstr /G:patterns.txt app.log

Output: (matching lines)

Anchors and exact-match flags

The line-position anchors /B, /E, and /X are simpler and more reliable than building anchors into regex patterns.

SwitchEquivalent regexUse case
/B "INFO"^INFOLines starting with INFO
/E "OK"OK$Lines ending with OK
/X "DONE"^DONE$Lines that are exactly "DONE"
cmd
rem Lines starting with a 4-digit year
findstr /B /R "[0-9][0-9][0-9][0-9]-" app.log

Output:

yaml
2026-05-22 09:12:33 ERROR Connection refused
2026-05-22 09:15:00 INFO  Service started
cmd
rem Find unfinished tasks — line ending in "PENDING"
findstr /E "PENDING" tasks.txt

Output:

arduino
Task 5: Implement OAuth callback   PENDING
Task 9: Migrate database           PENDING
cmd
rem Find exact "OK" markers (not "OKAY" or "FAILED OK")
findstr /X "OK" status.log

Output:

code
OK
OK
OK

Reading file lists (/F)

/F:filelist reads filenames from a text file (one per line) and searches each of them. This is the right way to constrain a recursive search to specific files generated elsewhere — typically by dir /B /S or where.

cmd
rem Build a file list then search those files
dir /B /S C:\Projects\myapp\*.py > pylist.txt
findstr /N /F:pylist.txt /C:"def main"

Output:

csharp
C:\Projects\myapp\main.py:12:def main():
C:\Projects\myapp\tools\runner.py:5:def main_loop():

Use /F in conjunction with where for cross-drive searches:

cmd
where /R C:\Tools *.bat > batfiles.txt
findstr /F:batfiles.txt /I /N "@echo off"

Output: (matches across discovered files)

Directory list (/D)

/D:dir1;dir2;dir3 searches in a semicolon-delimited list of directories without recursion into their subfolders. Useful when you want to scan several specific folders without the full overhead of /S.

cmd
findstr /D:"C:\Logs;C:\App\Logs;C:\Service\Logs" /I "fatal" *.log

Output: (matches across the three folders)

Character offset (/O)

/O reports the byte position of each matching line from the start of the file. This is useful for tools that consume positional data — for instance, to seek into a file at the exact match position.

cmd
findstr /O /N "ERROR" app.log

Output:

makefile
1234:47:2026-05-22 09:12:33 ERROR Connection refused
4567:103:2026-05-22 09:15:00 ERROR Timeout

The number before the line number is the byte offset.

findstr vs find

Windows also ships find.exe (not the same as Unix find). The two overlap but findstr is strictly more capable.

Featurefindfindstr
Literal string searchyes (default)yes (/L or default)
Regexnoyes (/R)
Recursive directory walkno/S
Multiple patternsnospace-separated or /G:
Case-insensitive/I/I
Invert match/V/V
Show line numbers/N/N
Count matches/Cno (use find /C /V "" after findstr)
File list inputno/F:
Anchorsno/B /E /X
Word boundaryno\< \> regex
cmd
rem find — only counts matches
find /C /V "" file.txt

Output:

diff
---------- FILE.TXT: 42
cmd
rem Common idiom: pipe findstr through find /C to count matches
findstr /I "error" app.log | find /C /V ""

Output:

code
17

The only place to prefer find over findstr is for simple counts via find /C /V "". Everything else, findstr does better.

findstr vs grep

findstr is the cmd.exe analogue of grep, but the two diverge significantly. The biggest practical differences are regex dialect, multi-pattern semantics, and recursive ignore handling.

Featurefindstrgrep (GNU)ripgrep (rg)
Default modeliteralregex (BRE)regex (Rust regex / PCRE2)
Regex flavourslimited BRE-likeBRE, ERE (-E), PCRE (-P)Rust regex, PCRE2 (-P)
Quantifiers* only*, +, ?, {n,m} (ERE/PCRE)full
Alternationspace-separated patterns`` in ERE/PCRE
Grouping ()noyesyes
Word boundary\< \>\b, \< \>, -w\b, -w
Shorthand \d \w \snoPCRE onlyyes
Case-insensitive/I-i-i, smart-case by default
Recursive/S-rdefault
File globsper file argument--include, --exclude-g (gitignore-style)
.gitignore respectnonoyes
Multi-line patternsno-z (NUL separator)--multiline
Context linesno-A -B -C-A -B -C
Show match onlyno-o-o
Filenames only/M-l-l
Invert/V-v-v
Line numbers/N-ndefault in recursive mode
Color output/A:hex--colorsmart default
Multi-threadednonoyes
Speed (large tree)slowfastvery fast

Recipe parity table

The same job in each tool:

cmd
rem Recursive search for "TODO" in .py files
findstr /S /R "TODO" *.py
grep -rn --include='*.py' 'TODO' .
rg -t py TODO

Output:

text
src\app.py:    # TODO: handle empty input
src\utils.py:    # TODO: refactor to async
tests\test_app.py:    # TODO: cover error path
cmd
rem Whole-word match, case-insensitive
findstr /I /R "\<error\>" app.log
grep -wi 'error' app.log
rg -wi error app.log

Output:

text
[2026-05-20 09:14:22] ERROR  connection refused
[2026-05-20 09:14:55] Error: timeout exceeded
cmd
rem Lines NOT containing "debug" or "trace"
findstr /V /R "debug trace" app.log
grep -vE 'debug|trace' app.log
rg -v -e debug -e trace app.log

Output:

text
[2026-05-20 09:14:22] INFO   server started on :8080
[2026-05-20 09:14:55] ERROR  connection refused
[2026-05-20 09:15:01] WARN   slow query (842ms)
cmd
rem Find files containing both "foo" AND "bar"
findstr /M "foo" *.log > foo.txt && for /f %f in (foo.txt) do findstr /M "bar" %f
grep -l 'foo' *.log | xargs grep -l 'bar'
rg -l foo | xargs rg -l bar

Output:

text
app.log
worker.log
cmd
rem Extract IPv4 addresses
findstr /R "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" access.log
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log
rg -o '(\d{1,3}\.){3}\d{1,3}' access.log

Output:

text
10.0.0.5
10.0.0.12
192.168.1.42

When to use which

  • findstr — when you are already in cmd.exe and don't want to install anything. Sufficient for simple log scans and config greps.
  • grep (in WSL or Git Bash) — when you need real regex, context lines, or you are scripting cross-platform.
  • ripgrep — when speed matters, when you want gitignore-aware recursion, or when you need PCRE2 with multi-line matching. The modern default for codebase search on any platform.

See sections/linux/grep.md and sections/linux/ripgrep.md for deep dives.

PowerShell equivalent: Select-String

Select-String is the modern PowerShell cmdlet for pattern search and the right replacement for findstr in any new script written in PowerShell. It supports full .NET regex, pipeline composition, structured output (with Filename, LineNumber, Matches), and context lines.

powershell
# Basic literal search
Select-String -Path app.log -SimpleMatch "ERROR"

Output:

c
app.log:3:2026-05-22 09:12:33 ERROR Connection refused
app.log:7:2026-05-22 09:15:00 ERROR Retry limit exceeded
powershell
# Regex search (default), case-insensitive (default)
Select-String -Path *.log -Pattern '\bERROR\b'

Output: (matches across all .log files)

powershell
# Recursive — combine with Get-ChildItem
Get-ChildItem -Path C:\Projects\myapp -Recurse -Include *.py |
    Select-String -Pattern 'def\s+main'

Output: (matches across the tree)

powershell
# Context lines (before and after)
Select-String -Path app.log -Pattern 'ERROR' -Context 2,3

Output:

yaml
> 2026-05-22 09:12:33 ERROR Connection refused
  2026-05-22 09:12:34 INFO  Retrying
  2026-05-22 09:12:35 INFO  Backoff 1s
  2026-05-22 09:12:36 INFO  Backoff 2s
powershell
# Invert match — non-matching lines
Select-String -Path app.log -Pattern 'DEBUG' -NotMatch

Output: (lines NOT matching DEBUG)

powershell
# Multiple patterns (OR)
Select-String -Path app.log -Pattern 'ERROR', 'FATAL', 'PANIC'

Output: (any matching line)

powershell
# Capture groups
Select-String -Path *.log -Pattern 'user=(\w+)' |
    ForEach-Object { $_.Matches.Groups[1].Value } |
    Sort-Object -Unique

Output:

code
alicedev
bobops
charlie
powershell
# Filenames only (like /M)
Select-String -Path C:\Configs\*.ini -Pattern 'ConnectionString' -List |
    Select-Object -ExpandProperty Path

Output:

makefile
C:\Configs\app.ini
C:\Configs\db.ini
powershell
# Count matches per file
Get-ChildItem C:\Logs\*.log |
    ForEach-Object {
        [PSCustomObject]@{
            File  = $_.Name
            Count = (Select-String -Path $_.FullName -Pattern 'ERROR').Count
        }
    } |
    Sort-Object Count -Descending

Output:

lua
File         Count
----         -----
app.log         47
worker.log      12
auth.log         3

Select-String advantages over findstr

  • Real regex — full .NET regex including \d, \w, lookarounds, capture groups, alternation, counted repetition.
  • Pipeline output — emits MatchInfo objects with Path, LineNumber, Line, Matches properties.
  • Context lines-Context Before,After.
  • Encoding awareness — pass -Encoding utf8 for non-default text encodings.
  • Capture group access.Matches.Groups[n].Value gives you parsed values, not just lines.

Select-String drawbacks

  • Slower startup than findstr (PowerShell init overhead).
  • No multi-threading — sequential file processing.
  • No .gitignore awareness. For codebase search, ripgrep is still faster.

findstr in pipelines and cmd quoting

findstr is the workhorse filter at the end of cmd.exe pipelines, used much like grep at the end of bash pipelines. Quoting differs between cmd and bash: cmd uses double quotes ("..."), single quotes are literal characters, and escape with ^ outside quotes or doubled-up "" inside.

cmd
rem Filter tasklist
tasklist /V | findstr /I "chrome"

rem Filter netstat
netstat -ano | findstr ":443.*LISTENING"

rem Filter ipconfig
ipconfig /all | findstr "IPv4"

rem Pipe service list
sc query | findstr /I "running"

rem Echo a literal pipe character inside a findstr argument
echo a^|b | findstr "^|"

Output (ipconfig /all | findstr "IPv4"):

css
   IPv4 Address. . . . . . . . . . . : 192.168.1.50(Preferred)

Quoting traps in cmd

cmd
rem WRONG — apostrophes are literal in cmd
findstr 'pattern' file.txt

rem RIGHT — use double quotes
findstr "pattern" file.txt

rem To include a literal double quote in pattern, double it up
findstr "say \"hi\"" file.txt   rem some Windows versions
findstr "say ""hi""" file.txt   rem most reliable

rem Pipes inside the pattern require escaping (or just use /C:)
findstr /C:"foo|bar" file.txt

Output: (per-file matches)

Performance considerations

findstr is single-threaded and operates on one file at a time even with /S. On large trees it is significantly slower than ripgrep or even grep under WSL. Specific performance traps:

  • Regex mode (/R) is slower than literal mode. If you don't need regex, omit /R.
  • Wildcards with /S rescan the directory tree per pattern argument when multiple patterns are passed without /G:.
  • Searching with /I on multibyte text is locale-dependent and slower than case-sensitive search.

For ad-hoc scripts that scan more than ~100 MB of text, prefer Select-String (which is comparable to findstr in speed but pipelines results) or install ripgrep for 10× speedups.

Common scenarios

List ports being listened on

cmd
netstat -ano | findstr "LISTENING" | findstr /R ":[0-9][0-9]*"

Output:

yaml
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       1432
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:8080           0.0.0.0:0              LISTENING       6124

Find which executable is using a port

cmd
netstat -ano | findstr ":3000" | findstr "LISTENING"

Output:

yaml
  TCP    0.0.0.0:3000           0.0.0.0:0              LISTENING       9876

Then look up the PID:

cmd
tasklist | findstr "9876"

Output:

arduino
node.exe                     9876 Console                    1     85,432 K

Grep registry export for a key

cmd
reg export HKLM\SOFTWARE\Microsoft\Windows tmp.reg
findstr /I /C:"DisableTaskMgr" tmp.reg

Output:

ini
"DisableTaskMgr"=dword:00000000

Find scripts containing a specific command

cmd
findstr /S /M /I /C:"taskkill" C:\Scripts\*.bat

Output:

makefile
C:\Scripts\cleanup.bat
C:\Scripts\nightly.bat

Count error severities in today's log

cmd
findstr /I "ERROR" C:\Logs\today.log | find /C /V ""
findstr /I "WARN"  C:\Logs\today.log | find /C /V ""
findstr /I "INFO"  C:\Logs\today.log | find /C /V ""

Output:

yaml
17
142
1893

Filter PowerShell transcript for cmdlet invocations

cmd
findstr /R "^PS C:" transcript.txt

Output:

vbnet
PS C:\Users\alicedev> Get-Service
PS C:\Users\alicedev> Restart-Service Spooler
PS C:\Users\alicedev> Stop-Process -Name notepad

Common pitfalls (extended)

  1. findstr returns code 0 for "no match" — actually it returns 1. Use if errorlevel 1 to detect no-match; the convention if not errorlevel 1 means "match found".
  2. CR-LF vs LF line endingsfindstr handles both, but the trailing \r is included in the matched line. Anchors like /E may behave unexpectedly if the line ends with \r\n and you anchored to a non-CR character.
  3. UTF-8 BOM at start of file breaks /B (start-of-line) anchor for the first line. Strip BOM with PowerShell Get-Content then re-save.
  4. /S follows reparse points and junctions — can loop on circular junctions. There is no --no-follow switch; use where or dir /S /B with explicit filtering.
  5. /G:patterns.txt file must have CRLF line endings — patterns saved with LF-only endings (e.g. by sed in Git Bash) get concatenated into one massive pattern.
  6. /A: colour codes don't work in PowerShell pipelines — colour escape sequences leak through to consumers. Use only in interactive cmd sessions.

Sources

References consulted while writing this article. Links open in a new tab.

  • Microsoft Learn — findstr command reference — Authoritative flag list and parameter semantics used to build the Essential options table.
  • SS64 — findstr — Cross-version comparison and historical syntax notes.

Tips

Default findstr is case-sensitive. Always add /I for log scans unless you specifically need exact case — Windows logs use mixed-case ("Error", "ERROR", "error") interchangeably.

Use /C:"phrase with spaces" for any pattern containing spaces. Without /C:, the words become OR-separated patterns and you'll get unexpected matches.

Pipe through find /C /V "" to count matching lines: findstr "error" file | find /C /V "". This is the cmd.exe equivalent of grep -c.

For any non-trivial pattern search, jump to Select-String in PowerShell. The full .NET regex engine and structured output are worth the slightly longer command line.

[!WARN] findstr's regex dialect is missing +, ?, {n,m}, |, (), \d, \w, \s, and lookarounds. If you're hitting those limits, switch to Select-String or ripgrep — don't try to work around findstr's limitations.

[!WARN] Multiple space-separated patterns are OR, not AND. To AND, pipe findstr into itself: findstr "foo" file | findstr "bar".