grep Cheatsheet - Linux Text Search Reference

Fishing an error out of a log, finding every reference to an identifier, or testing a condition in a script all reduce to searching lines by pattern, and four flags cover most of it: -r, -i, -C n for context, and -E for extended regex. Mastering those four dissolves the most common complaint about grep — finding the match but missing the context around it. The sheet spans basic search, recursion, regular expressions, context, multiple patterns and common patterns. The multi-pattern group gives the escape hatches worth knowing: repeating -e, reading patterns from a file with -f, and constraining the file set with --include and --exclude. One performance note: sweeping node_modules, .git or dist without filtering makes --exclude-dir a requirement rather than an optimisation. The regex dialect trap matches sed: under BRE, +, ? and | need escaping unless you pass -E. The boundary is drawn in the scenario group: grep filters lines, awk aggregates columns, and rg respects .gitignore.

SysOps·38 commands·Last updated 2026-07-21
grepText Searchlinux

Typical Use Case

For developers who search logs and code every day. grep has three traps worth stating up front: the default is BRE, where + ? and | must be escaped or -E used; you cannot drop -o when extracting fragments, or counting will be done per line rather than per match; and recursive searches of a large tree need --exclude-dir for node_modules and .git or a single search can stall for minutes. This sheet is organised into six groups and works well as a troubleshooting checklist.

Recursive 7

grep -r "pattern" dir/
Recursively search a directory
grep -R "pattern" dir/
Recursive search (follow symlinks)
grep -rn "pattern" dir/
Recursive with line numbers
grep -rl "pattern" dir/
Output only matching file names
grep -r --include="*.log" "pattern" dir/
Search only a given file type
grep -r --exclude="*.tmp" "pattern" dir/
Exclude a given file type
grep -r --exclude-dir="node_modules" "pattern" dir/
Exclude a directory

Regular Expression 9

grep -E "pattern" file
Extended regex (equivalent to egrep)
grep "^start" file
Match line start
grep "end$" file
Match line end
grep "\<word\>" file
Match word boundary
grep -w "word" file
Match whole word
grep "[0-9]" file
Match digits
grep "[^0-9]" file
Match non-digits
grep "a\|b" file
Match a or b (BRE)
grep -E "a|b" file
Match a or b (ERE)

Context 4

grep -A 3 "pattern" file
Show 3 lines after each match
grep -B 2 "pattern" file
Show 2 lines before each match
grep -C 3 "pattern" file
Show 3 lines before and after each match
grep -A 3 -B 2 "pattern" file
2 lines before and 3 after

Multiple Patterns & Files 6

grep -e "pattern1" -e "pattern2" file
Match multiple patterns
grep -f patterns.txt file
Read patterns from a file
grep "pattern" file1 file2
Search multiple files
grep "pattern" *.log
Search files matched by a wildcard
grep -H "pattern" file
Show file name
grep -h "pattern" file1 file2
Suppress file name

Common Patterns 5

grep "error\|warning" app.log
Search for error or warning
grep -rn "TODO\|FIXME" src/
Find TODO/FIXME in code
grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" access.log
Extract IP addresses
grep -c "^$" file
Count empty lines
grep -v "^#" file | grep -v "^$"
Exclude comments and blank lines

Parameter matrix

ParameterEffectExample
-i / -vIgnore case / invert matchgrep -i "error" app.log
-cCount matching lines (without outputting content)grep -c "^$" file
-n / -HShow line numbers / show file namesgrep -rn "TODO" src/
-oOutput only the matched fragment instead of the whole linegrep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" access.log
-qQuiet mode, returns only the status code (for scripts)grep -q "ready" app.log
-r / -RRecursive / recursive following symlinksgrep -r "pattern" dir/
--include / --excludeLimit or exclude file typesgrep -r --include="*.log" "err" dir/
--exclude-dirExclude directories, essential for searching large projectsgrep -r --exclude-dir="node_modules" "x" .
-EExtended regex (+ ? | need no escaping)grep -E "a|b" file
-w / \< \>Match a whole word rather than part of a wordgrep -w "cat" file
-A / -B / -CShow the following n lines / preceding n lines / n lines on both sidesgrep -C 3 "FATAL" app.log
-e / -fMultiple patterns / read patterns from a filegrep -e "p1" -e "p2" file
-l / -hOutput only file names / do not output file namesgrep -rl "pattern" dir/

Common pitfalls

SymptomYou wrote + ? | in the pattern but it does not match.

Causegrep uses basic regular expressions (BRE) by default, and these three symbols are literals under BRE.

FixAdd -E to enable extended regex, or escape these symbols as \+ \? \|.

SymptomYou want to count how many times something occurs, but the result is far larger than the actual number.

CauseWithout -o, uniq counts whole lines rather than matched fragments.

FixAdd -o to output only the matched part: grep -oE "pattern" file | sort | uniq -c | sort -rn.

SymptomSearching "cat" also matches concatenate.

CauseThe pattern has no word-boundary constraint.

FixAdd -w (or use \<cat\> under BRE) to restrict to a whole word.

SymptomA recursive search over the project directory hangs with no result for minutes.

CauseIt scanned directories with huge numbers of small files such as node_modules and .git.

FixAdd --exclude-dir="node_modules" --exclude-dir=".git" to limit the scope.

SymptomSearching for a dot in a filename matched any character.

Cause. is a regex metacharacter and was used literally without escaping.

FixWrite it escaped as \. or use grep -F for literal matching.

SymptomA grep test in a script always reports "no match".

CauseThe grep output was treated as the return value, but it actually outputs content and expresses the result via the exit code.

FixUse grep -q to check only the exit code: if grep -q "pattern" file; then ... fi.

SymptomIt matched in the logs, but a line with multiple occurrences is counted only once.

Causegrep -c counts matching lines rather than the number of matches.

FixFor occurrence counts, extract with grep -o then wc -l, or use grep -o | wc -l.

Troubleshooting

  1. 1First see what actually matched

    grep -o "pattern" file

    -o outputs only the matched fragment; no output means no match, so simplify the pattern piece by piece and retry.

  2. 2Locate the error position with context

    grep -n -C 3 "FATAL" app.log

    -n gives line numbers and -C 3 shows three lines on both sides; usually these lines explain the problem.

  3. 3Limit the search scope by type

    grep -rn --include="*.ts" "TODO" src/

    Limiting file types is far faster than searching everything and filtering afterward.

  4. 4Check whether content exists in a script

    grep -q "ready" app.log

    Quiet mode returns only the status code and is suited to health checks.

  5. 5Count occurrences of each item

    grep -oE "error|warning" app.log | sort | uniq -c | sort -rn

    -o must not be omitted; sort first so equal values are adjacent, then count.

Command Examples

Recursive search with context

grep -rn -C 2 "handleError" src/

-C 2 displays 2 lines before and after the match and -n gives line numbers for jumping. When nothing is found, first check case sensitivity, since grep is case-sensitive by default.

Output

src/api/client.ts-42-  try {
src/api/client.ts-43-    const res = await fetch(url)
src/api/client.ts:44-    handleError(res)
--
src/api/retry.ts-18-      handleError(new Error("timeout"))

Extract and count IP addresses from a log

grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' access.log | sort | uniq -c | sort -rn | head

-o outputs only the matched part rather than the whole line, which is required to feed sort | uniq -c for counting. Without -o you get whole lines and counting is meaningless.

Output

   1842 203.0.113.7
    931 198.51.100.22
    407 192.0.2.180

Restrict to file types and skip dependency directories

grep -rn --include="*.ts" --include="*.js" --exclude-dir=node_modules "API_KEY" .

--include can be specified repeatedly; --exclude-dir skips large directories like node_modules, which is faster and keeps results from being drowned in dependency code.

Common Pitfalls

  • grep is case-sensitive by default; try -i when nothing matches.
  • Metacharacters (. * + ? [ ]) are interpreted as regex, so searching a.b also matches axb — use -F or escape them.
  • Recursive searches without --exclude-dir=node_modules are slow and swamped by dependency code.
  • grep -r does not follow symlinks, so content behind a symlinked dependency directory is missed; use -R.
  • When extracting with -o, do not forget the flag — otherwise uniq counts whole lines rather than the matched fragments.

Tips

  • grep uses BRE (basic regex) by default; +, ?, | must be escaped or enable ERE with -E.
  • When searching large directories recursively, exclude node_modules, .git, etc. with --exclude-dir.
  • grep -q is useful in scripts to test for a match — it outputs nothing and returns only an exit code.

FAQ

What is the difference between grep -r and grep -R?

Lowercase -r recurses through normal directories and files but does not follow symlinks; uppercase -R does follow them. If node_modules or ~/.cache in your repo contains symlinks pointing elsewhere, -r silently misses their contents while -R finds them. On GNU grep -r is essentially --recursive, though BSD grep on macOS treats them the same.

How do I search only within certain file types?

Use --include: grep -rn --include="*.log" "error" . restricts the search to .log files. Chain several with --include="*.ts" --include="*.js". Symmetrically, --exclude lets you skip directories like node_modules or dist so results are not buried in dependency code.

Why does grep fail to match a string that is clearly there?

Most often it is case: grep is case-sensitive by default and needs -i. Next, metacharacters are being interpreted — searching a.b also matches axb, so add -F for a fixed string or escape the metacharacters. A third cause is invisible characters or CRLF line endings defeating a $ anchor, which you can isolate by grepping for a carriage return directly.

When should I use grep -n, -c, -l or -o?

-n prints line numbers for quick navigation and is the flag you add most. -c outputs only the count of matching lines, ideal for scripted checks. -l lists just the filenames that contain a match, useful for scoping a bulk edit. -o prints only the matched portion itself, which piped into sort | uniq -c gives you frequency counts of tokens in a log.

How do I match several keywords at once?

Two ways. Piping is the clearest: grep "error" app.log | grep "timeout" filters sequentially. Or pass several patterns with -e in one pass: grep -e "error" -e "timeout" app.log, which scans the file only once and is faster. With many patterns, read them from a file via -f patterns.txt.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us