Skip to main content

How to Extract Lines Matching a Pattern from a File in Batch Script

Text filtering is the cornerstone of system administration. Whether you need to pull "ERROR" alerts out of a massive daily log, find all local users in an export file, or generate a filtered subset of telemetry data, extracting lines that match a specific pattern is a task you will perform constantly.

In this guide, we will demonstrate how to extract matching lines using native Batch commands (find and findstr) as well as PowerShell (Select-String).

Method 1: The Simple Literal Match (find)

If you are looking for a single, exact string (like "ERROR:"), the find command is the fastest and simplest native tool. It treats the search string exactly as typed.

Implementation Script

@echo off
setlocal enabledelayedexpansion

set "LogFile=server_events.log"
set "ErrorLog=extracted_errors.log"

:: Verify source file exists
if not exist "%LogFile%" (
echo [ERROR] Log file "%LogFile%" not found.
pause
exit /b 1
)

echo Scanning "%LogFile%" for errors...

:: /i ignores case (matches Error, ERROR, error)
:: Reading from stdin with < avoids printing a filename header
find /i "ERROR:" < "%LogFile%" > "%ErrorLog%"

:: Capture the exit code immediately
set "findResult=!errorlevel!"

:: Check the exit code
:: 0 = Matches found
:: 1 = No matches found
:: 2+ = Error occurred
if !findResult! equ 0 (
echo [SUCCESS] Errors extracted to "%ErrorLog%".
) else if !findResult! equ 1 (
echo [INFO] Clean log. No errors found.
:: Optional: clean up the empty output file
del "%ErrorLog%" 2>nul
) else (
echo [ERROR] The search encountered a problem.
pause
exit /b 1
)

endlocal
pause
exit /b 0
warning

The find command searches for literal strings. It does not natively support searching for "Word A OR Word B" in a single pass without taking advantage of piping (type file | find "A" | find "B" actually functions as AND, not OR). For multi-word matching, use findstr.

Method 2: The Multi-Pattern Search (findstr)

The findstr command is much more powerful. It can search for multiple distinct words simultaneously (acting as an "OR" search) and supports basic regular expressions.

Implementation Script

@echo off
setlocal enabledelayedexpansion

set "Source=app_trace.txt"
set "Dest=critical_events.txt"

if not exist "%Source%" (
echo [ERROR] Source file "%Source%" missing.
pause
exit /b 1
)

echo Extracting Warning and Critical lines...

:: Behavior of findstr without /c:
:: A space between words means "OR". The below command matches
:: any line containing the word "WARN" OR the word "CRITICAL".
:: /i = ignore case
findstr /i "WARN CRITICAL" "%Source%" > "%Dest%"

:: Capture the exit code immediately
set "findstrResult=!errorlevel!"

:: If you need an exact phrase WITH a space, use /c:
:: findstr /i /c:"ACCESS DENIED" /c:"FATAL CRASH" "%Source%" > "%Dest%"

if !findstrResult! equ 0 (
echo [SUCCESS] Matches saved to "%Dest%".
) else if !findstrResult! equ 1 (
echo [INFO] No matched lines found.
del "%Dest%" 2>nul
) else (
echo [ERROR] The search encountered a problem.
pause
exit /b 1
)

endlocal
pause
exit /b 0
tip

Regex with findstr: You can use the /r flag to enable regular expressions. For instance, findstr /r /i "^[0-9].*ERROR" "%Source%" will extract lines that begin with a number and contain the word ERROR anywhere further down the line.

Method 3: Advanced Regex with PowerShell (Select-String)

While findstr supports basic regex, its implementation is limited and has well-known edge-case bugs (such as failing on certain POSIX character class bounds). If you need complex pattern matching (like extracting IP addresses or specific email formats), use PowerShell's Select-String.

Implementation Script

@echo off
setlocal enabledelayedexpansion

set "Source=access.log"
set "Dest=ip_addresses.txt"

if not exist "%Source%" (
echo [ERROR] Source file "%Source%" missing.
pause
exit /b 1
)

echo Extracting lines containing IP addresses...

:: The regex \b\d{1,3}(\.\d{1,3}){3}\b matches an IPv4 address structure.
:: Note: Select-String outputs MatchInfo objects. To just get the raw matching
:: line text, we expand the 'Line' property.
powershell -NoProfile -Command ^
"Select-String -Path '%Source%' -Pattern '\b\d{1,3}(\.\d{1,3}){3}\b' | " ^
"Select-Object -ExpandProperty Line | " ^
"Set-Content -Path '%Dest%' -Encoding UTF8"

:: Capture the exit code immediately
set "psResult=!errorlevel!"

if !psResult! equ 0 (
echo [SUCCESS] Matches successfully dumped to "%Dest%".
) else (
echo [INFO] No matches found or script failed.
pause
exit /b 1
)

endlocal
pause
exit /b 0

Comparisons: The Right Tool for the Job

FeaturefindfindstrSelect-String (PS)
SpeedVery FastFastSlower (Overhead)
LogicLiteral matchingBasic Regex / Multi-word ORFull .NET Regex
Exact PhrasesBuilt-in (Default)Requires /c:"phrase"Put pattern in quotes
Unicode / UTF-16Fails / CorruptedFails / CorruptedSupported

Best Practices

  1. Interpret ErrorLevel 1 Correctly: For all three methods, errorlevel 1 typically means "No matches were found in the file." This is not necessarily a bug or a script failure: it is a valid data result. Handle it gracefully, and optionally delete the empty output file if no matches were written.
  2. Watch the File Encoding: The native find and findstr utilities are built for legacy ANSI/ASCII files (or UTF-8 without BOM). If you try to run findstr on an uncoerced text file generated by a native PowerShell pipeline (which defaults to UTF-16 LE), it will fail to find matches because the byte structure is different. In those cases, use Method 3 (Select-String).
  3. Use /c: in findstr Carefully: If your search string has a space in it (e.g., Failed Login), using findstr "Failed Login" searches for "Failed" OR "Login". You MUST use findstr /c:"Failed Login" to force the space to be evaluated literally.
  4. Use Output Redirection Intelligently: When using find, passing the file as an argument (find "keyword" file.txt) will print a header containing the file name. Supplying standard input with the < operator (find "keyword" < file.txt) extracts pure matching data with no filename headers, which is critical for clean filtering.

Conclusion

Knowing how to extract the gold from the gravel allows you to automate monitoring, auditing, and troubleshooting far more effectively. For rapid, literal filtering, find and findstr give you the native performance you need. For nuanced, complex text parsing, leaning on PowerShell ensures that you are never limited by the boundaries of legacy tools.