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 / -v忽略大小写 / 反向排除grep -i "error" app.log
-c统计匹配行数(不输出内容)grep -c "^$" file
-n / -H显示行号 / 显示文件名grep -rn "TODO" src/
-o只输出匹配片段而非整行grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" access.log
-q静默模式,只返回状态码(脚本用)grep -q "ready" app.log
-r / -R递归 / 递归并跟随符号链接grep -r "pattern" dir/
--include / --exclude限定或排除文件类型grep -r --include="*.log" "err" dir/
--exclude-dir排除目录,搜大项目必备grep -r --exclude-dir="node_modules" "x" .
-E扩展正则(+ ? | 无需转义)grep -E "a|b" file
-w / \< \>匹配完整单词而非词内部grep -w "cat" file
-A / -B / -C显示后 n 行 / 前 n 行 / 前后各 n 行grep -C 3 "FATAL" app.log
-e / -f多个模式 / 从文件读模式grep -e "p1" -e "p2" file
-l / -h只输出文件名 / 不输出文件名grep -rl "pattern" dir/

Common pitfalls

Symptom模式里写了 + ? | 却匹配不到。

Causegrep 默认使用基本正则(BRE),这三个符号在 BRE 下是字面量。

Fix加 -E 启用扩展正则,或把这些符号转义成 \+ \? \|。

Symptom想统计某项出现多少次,结果比实际多很多。

Cause没加 -o,uniq 统计的是整行而非匹配片段。

Fix加 -o 只输出匹配部分:grep -oE "模式" file | sort | uniq -c | sort -rn。

Symptom搜 "cat" 结果把 concatenate 也匹配出来了。

Cause模式没有单词边界约束。

Fix加 -w(或 BRE 下用 \<cat\>)限定为完整单词。

Symptom递归搜项目目录卡住,几分钟不出结果。

Cause扫进了 node_modules、.git 等海量小文件目录。

Fix加 --exclude-dir="node_modules" --exclude-dir=".git" 限定范围。

Symptom搜文件名里的点号,结果匹配到了任意字符。

Cause.` 是正则元字符,字面使用未转义。

Fix写成 \. 转义,或用 grep -F 按字面匹配。

Symptom脚本里 grep 的判断结果总是「不匹配」。

Cause把 grep 的输出当成返回值,实际它输出内容并以退出码表达结果。

Fix用 grep -q 只看退出码:if grep -q "pattern" file; then … fi。

Symptom搜到日志里了,但一行里有多个匹配时只算一次。

Causegrep -c 统计的是匹配行数而非匹配次数。

Fix需要出现次数时用 grep -o 提取后 wc -l,或用 grep -o | wc -l。

Troubleshooting

  1. 1先看实际匹配到了什么

    grep -o "pattern" file

    -o 只输出匹配片段;没输出就说明没命中,可逐段简化模式再试。

  2. 2带上下文定位报错位置

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

    -n 给行号、-C 3 前后各三行,通常这几行就说明了问题。

  3. 3按类型限定搜索范围

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

    限定文件类型比全量搜后再过滤快得多。

  4. 4脚本里判断内容是否存在

    grep -q "ready" app.log

    静默模式只返回状态码,适合写进健康检查。

  5. 5统计各项出现次数

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

    -o 不能省;先排序让相同值相邻再计数。

Command Examples

Recursive search with context

grep -rn -C 2 "handleError" src/

-C 2 表示匹配行前后各显示 2 行,-n 给行号便于跳转。找不到时先确认大小写——grep 默认区分大小写。

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 只输出匹配到的部分而非整行,这样才能喂给 sort | uniq -c 统计。不加 -o 拿到的是整行,计数没有意义。

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 可重复指定;--exclude-dir 跳过 node_modules 之类的大目录,既快又避免结果被依赖代码淹没。

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