grep技术要点
1) 工作模型(3 件事)
- 输入 :从文件 或标准输入 (
-
)读入,一次按"行"处理(除非用-z
改成以 NUL 作为"行"分隔)。 - 匹配 :把每一行拿去和模式 (pattern)比对。模式可以是正则 (BRE/ERE/PCRE)或纯文本 (
-F
)。 - 输出 :默认打印匹配到的整行 ;用
-o
可只打印匹配片段;用-l/-L/-c
改为打印文件名 或计数 。
退出码 (脚本很有用):匹配到返回0
,未匹配返回1
,出错(且没-q
)返回2
。
2) 模式种类 & 选型思路
-G
(BRE,默认 ):基础正则。+ ? | ()
在 BRE 里不是 元字符,需转义:\+ \? \| \(\)
.-E
(ERE):扩展正则,+ ? | ()
直接可用;日常首选 ,等价于历史上的egrep
。-F
(固定字符串):把模式当"纯文本 ",不解正则;最快、最稳,适合日志多关键词查找。-P
(Perl/PCRE):支持高级正则 (如\b
、断言(?<=x)
、懒惰量词等)。有时未编进发行版,或在极大文件上稍慢------仅当确实需要这些能力再用。
通用小抄(ERE 语义为例)
^
行首;$
行尾;.
任意单字符;*
前导重复 ≥0 次;+
≥1 次;?
0或1;{m,n}
次数;[]
字符类;[^]
取反;()
分组;|
或;[[:alpha:]] [[:digit:]] [[:space:]]
等POSIX 类 对多语言更稳。
整词 :-w
(按"字母/数字/下划线"边界)或-P '\bword\b'
。
多模式 :多次-e
,或-f pattern_file
(每行一个模式)。
3) 递归与路径筛选
-r
递归;-R
递归且跟随符号链接。默认无文件时,递归会把"当前目录"当作输入。- 只搜特定文件 :
--include='*.{c,h}'
;排除 :--exclude='*.min.js'
、--exclude-dir={.git,node_modules,build}
。 - 搭配
-n
(行号)、-H/-h
(是否加文件名前缀)提升可读性。
4) 输出控制与上下文
- 只要文件名 :
-l
(有匹配的文件);-L
(没有匹配的文件)。 - 只要数量 :
-c
;只要命中片段 :-o
(常与-h
连用,便于做统计)。 - 上下文 :
-A NUM
(后文)、-B NUM
(前文)、-C NUM
(两侧),或简写-NUM
。 - 限流 :
-m NUM
(每个文件匹配到 NUM 行就停),提高速度。 - 颜色 :
--color=auto
(或--colour
)。管道里可能被判为非 TTY,颜色会自动关闭。
5) 大文件/二进制/编码细节
- 二进制检测触发时会出现"Binary file matches"。
- 当你只是想按文本看:用
-a
或--binary-files=text
。 - 想忽略 二进制:
-I
(等价--binary-files=without-match
)。
- 当你只是想按文本看:用
-z
:把 NUL 当"换行",配合find -print0 / xargs -0
进行安全文件名 流水线;-Z
输出文件名后加 NUL。- 速度技巧:
LC_ALL=C grep -F ...
(字节序比较、不开多字节;非常快,但不适合需要本地化字符类/大小写折叠的场景)。
6) 易踩的坑(超重要)
- 一定要正确引用模式 :在 shell 里
* ? () | $ \
都可能被解释,用单引号 包裹模式最稳:'foo.*bar'
。 - BRE/ERE 转义差异 :在
-G
下+ ? | ()
需转义;改成-E
通常更直观。 -P
可用性 :有些系统未启用 PCRE;脚本里用-P
前先确认环境。- 递归与链接 :
-R
可能跟随循环链接导致慢或"走不出来";需要时用--exclude-dir
限制。 -w
的"词边界"定义 :只把字母/数字/下划线当词字符;含连字符或点号的"词"不适合-w
,改用-P '\b'
或显式边界。- 大小写与本地化 :
-i
在多字节/本地化下可能和你以为的不完全一致;要"字节级"行为就LC_ALL=C
。
7) 选型口诀
- 纯文本多关键词 :
-F
或-Ff
(最快)。 - 一般正则 :优先
-E
(少转义,读写性好)。 - 需要断言/词边界 :
-P
(确认环境支持)。 - 递归找代码 :
-Rni --include=... --exclude-dir=...
。 - 只要文件名/计数 :
-l / -L / -c
。 - 抽取值 :
-o
(必要时-P
断言)。 - 大文件提速 :
-m1
(命中即停)、LC_ALL=C
、缩小--include/--exclude
范围。
命令详解
主要用法如下:
grep [OPTION]... PATTERNS [FILE]...
要点 :命令基本形式。PATTERNS
是要匹配的模式(可以是正则或字符串);FILE
是一个或多个文件名。
示例:
bash
grep 'error' /var/log/syslog
Search for PATTERNS in each FILE.
要点 :对每个文件逐行检查模式,默认输出匹配到的整行(除非用 -o
或其它选项改变输出)。
示例:
bash
grep 'TODO' main.c util.c
Example: grep -i 'hello world' menu.h main.c
要点 :示例说明 -i
(忽略大小写)如何使用。
示例(同上):
bash
grep -i 'hello world' menu.h main.c
PATTERNS can contain multiple patterns separated by newlines.
要点 :一个模式文件或多行模式串中每行可视为一个独立模式;任一行匹配即为命中。
示例 :
假设 patterns.txt
内容为:
ERROR
WARN
FATAL
则:
bash
grep -Ff patterns.txt app.log
会查找包含任意一行模式的日志行。
模式选择与解释
-E, --extended-regexp PATTERNS are extended regular expressions
要点 :使用扩展正则(ERE)。在 ERE 中,+ ? | () {}
等元字符无需反斜杠转义,写法更直观。
示例:
bash
grep -E 'foo|bar|baz' file.txt
-F, --fixed-strings PATTERNS are strings
要点 :把模式当作纯文本 匹配,不解析正则,速度最快,适合关键词列表。
示例:
bash
grep -F 'a.b' file.txt # 匹配字面 "a.b"
-G, --basic-regexp PATTERNS are basic regular expressions
要点 :使用基本正则(BRE,很多系统默认)。在 BRE 中某些元字符(如 + ? | ()
)需要转义。
示例:
bash
grep -G 'foo\|bar' file.txt # 在 BRE 中 "或" 用 \|
-P, --perl-regexp PATTERNS are Perl regular expressions
要点 :启用 PCRE(Perl 风格正则),支持断言、\b、懒惰量词等高级特性。注意并非所有系统都启用了 -P
。
示例:
bash
grep -Po '(?<=ID=)\d+' data.txt # 提取 ID= 后的数字
-e, --regexp=PATTERNS use PATTERNS for matching
要点 :显式指定一个模式;可重复使用 -e
指定多个模式;适合模式以 -
开头时避免被误解析为选项。
示例:
bash
grep -e '^Error' -e '^Warning' logfile
-f, --file=FILE take PATTERNS from FILE
要点 :从文件读取多个模式(文件每行一个模式),和 -e
等效但便于管理大量模式。
示例:
bash
grep -Ff keywords.txt big.log
-i, --ignore-case ignore case distinctions in patterns and data
要点 :忽略大小写匹配(模式与数据均不区分大小写)。
示例:
bash
grep -i 'linux' README
--no-ignore-case do not ignore case distinctions (default)
要点 :恢复区分大小写(这是默认行为),通常无需显式指定。
示例:
bash
grep --no-ignore-case 'Makefile' files.list
-w, --word-regexp match only whole words
要点 :匹配完整单词,单词边界按"字母/数字/下划线"定义。连字符或点不被视为词字符。
示例:
bash
grep -w 'is' file.txt # 不会匹配 "this"
-x, --line-regexp match only whole lines
要点 :行内容必须完全等于模式才匹配(整行匹配)。
示例:
bash
grep -x 'enabled=true' config.txt
-z, --null-data a data line ends in 0 byte, not newline
要点 :把 NUL(\0)当作行分隔符而非换行,用于处理包含换行的记录或与 find -print0
、xargs -0
联动时更安全。
示例:
bash
# 与 -Z/-0 搭配用于 NUL 安全的文件名流
find . -type f -print0 | xargs -0 grep -z 'pattern'
杂项
-s, --no-messages suppress error messages
要点 :抑制错误输出(如权限错误或不存在的文件),脚本中用于避免噪音。
示例:
bash
grep -s 'secret' /root/*
-v, --invert-match select non-matching lines
要点 :反选,输出不匹配模式的行,用于过滤包含某关键词的行之外的内容。
示例:
bash
grep -v 'DEBUG' app.log
-V, --version display version information and exit
要点 :显示 grep
的版本然后退出。
示例:
bash
grep -V
--help display this help text and exit
要点 :显示帮助文本并退出。
示例:
bash
grep --help
输出控制
-m, --max-count=NUM stop after NUM selected lines
要点 :在每个输入文件上匹配到 NUM
行后停止处理该文件(提高速度、用于"是否存在"检查)。
示例:
bash
grep -m1 'fatal' big.log
-b, --byte-offset print the byte offset with output lines
要点 :输出每个匹配行在文件中的字节偏移(从 0 开始),便于定位二进制或精确定位。
示例:
bash
grep -bn 'pattern' file.txt
-n, --line-number print line number with output lines
要点 :输出匹配行的行号(文件名:行号:内容)。
示例:
bash
grep -n 'TODO' src/*.c
--line-buffered flush output on every line
要点 :开启逐行刷新,适用于实时管道(但会降低性能)。
示例:
bash
tail -f app.log | grep --line-buffered 'ERROR'
-H, --with-filename print file name with output lines
要点 :在输出前加上文件名;在多文件搜索时默认启用,但可强制显示。
示例:
bash
grep -H 'alloc' src/*.c
-h, --no-filename suppress the file name prefix on output
要点 :不显示文件名(适合单文件或只想要匹配行本身)。
示例:
bash
grep -h 'pattern' file1 file2
--label=LABEL use LABEL as the standard input file name prefix
要点 :当从标准输入读取时,用给定标签代替文件名显示,便于记录来源。
示例:
bash
cat data | grep --label=STDIN -H 'pattern'
-o, --only-matching show only nonempty parts of lines that match
要点 :只输出匹配到的子串,不输出整行。常用于抽取 URL、数字、键值等。
示例:
bash
grep -Eo 'https?://[^ ]+' page.html
-q, --quiet, --silent suppress all normal output
要点 :静默模式,不打印任何匹配内容,用退出码判断是否存在匹配(适合脚本判断)。
示例:
bash
if grep -q 'READY' status.txt; then echo OK; fi
--binary-files=TYPE assume that binary files are TYPE; TYPE is 'binary', 'text', or 'without-match'
要点:设置遇到二进制文件时的处理策略:
binary
:按二进制对待(默认;匹配时通常显示 "Binary file matches");text
:把二进制当作文本处理;without-match
:把二进制视为没有匹配(忽略)。
示例:
bash
grep --binary-files=text 'PNG' image.bin
grep --binary-files=without-match 'pattern' *
-a, --text equivalent to --binary-files=text
要点 :等价于把二进制文件当作文本处理。
示例:
bash
grep -a 'TODO' file.bin
-I equivalent to --binary-files=without-match
要点 :等价于把二进制文件视为不匹配(忽略)。
示例:
bash
grep -I 'pattern' *
-d, --directories=ACTION how to handle directories; ACTION is 'read', 'recurse', or 'skip'
要点:指定当遇到目录时的处理方式:
read
:把目录当文件读(很少用);recurse
:进入目录递归搜索;skip
:跳过目录。
示例:
bash
grep -d skip 'needle' .
-D, --devices=ACTION how to handle devices, FIFOs and sockets; ACTION is 'read' or 'skip'
要点 :控制如何处理设备、FIFO、套接字,避免阻塞或不必要读取。
示例:
bash
grep -D skip -R 'pattern' /
-r, --recursive like --directories=recurse
要点 :递归搜索目录(等同 --directories=recurse
)。默认不跟随符号链接。
示例:
bash
grep -rn 'TODO' src/
-R, --dereference-recursive likewise, but follow all symlinks
要点 :递归并跟随所有符号链接(注意可能形成循环,需要配合 --exclude-dir
限制)。
示例:
bash
grep -Rni --exclude-dir=.git 'init' .
--include=GLOB search only files that match GLOB (a file pattern)
要点 :只在匹配指定文件名模式的文件中搜索,有助于提速并限制范围。支持通配如 *.c
或 {*.c,*.h}
。
示例:
bash
grep -Rni --include='*.{c,h,cpp}' 'allocator' .
--exclude=GLOB skip files that match GLOB
要点 :排除符合该模式的文件(如 .min.js
、二进制等)。
示例:
bash
grep -Rni --exclude='*.min.js' 'fetch' web/
--exclude-from=FILE skip files that match any file pattern from FILE
要点 :从文件读取排除模式列表(每行一个 GLOB),便于管理复杂排除规则。
示例:
bash
grep -Rni --exclude-from=.greprules 'needle' .
--exclude-dir=GLOB skip directories that match GLOB
要点 :排除指定目录(常用 .git
、node_modules
、build
等)。
示例:
bash
grep -Rni --exclude-dir={.git,node_modules,dist} 'config' .
-L, --files-without-match print only names of FILEs with no selected lines
要点 :只列出没有 匹配的文件名,常用于查找缺失项(如缺少版权头的文件)。
示例:
bash
grep -RL 'Copyright' src/
-l, --files-with-matches print only names of FILEs with selected lines
要点 :只列出有 匹配的文件名(方便后续批处理)。
示例:
bash
grep -Rl 'panic' /var/log
-c, --count print only a count of selected lines per FILE
要点 :对每个文件输出匹配行数,不显示具体行。
示例:
bash
grep -Rc 'ERROR' logs/
-T, --initial-tab make tabs line up (if needed)
要点 :输出格式对齐(在带文件名前缀时用 Tab 对齐)。
示例:
bash
grep -TnH 'pattern' *.txt
-Z, --null print 0 byte after FILE name
要点 :在文件名后输出 NUL(\0),便于以 NUL 为分隔的管道安全处理,例如与 xargs -0
联动。
示例:
bash
grep -Zl 'needle' -R . | xargs -0 -I{} echo "FOUND: {}"
上下文控制
-B, --before-context=NUM print NUM lines of leading context
要点 :输出匹配行以及该行之前的 NUM
行,便于查看上下文。
示例:
bash
grep -B 2 'OutOfMemoryError' app.log
-A, --after-context=NUM print NUM lines of trailing context
要点 :输出匹配行以及之后的 NUM
行。
示例:
bash
grep -A 2 'OutOfMemoryError' app.log
-C, --context=NUM print NUM lines of output context
要点 :-C NUM
等价于同时指定 -B NUM
和 -A NUM
,显示匹配行前后 NUM
行。
示例:
bash
grep -C 3 'OutOfMemoryError' app.log
-NUM same as --context=NUM
要点 :简写形式,例如 -3
相当于 -C 3
。
示例:
bash
grep -R3 'failed' logs/
--group-separator=SEP print SEP on line between matches with context
要点 :当多组匹配(有上下文)时,在组间插入自定义分隔符 SEP
。默认分隔是 --
。
示例:
bash
grep -C1 --group-separator='-----' 'error' app.log
--no-group-separator do not print separator for matches with context
要点 :取消组间分隔(把多个匹配块连续显示)。
示例:
bash
grep -C2 --no-group-separator 'warn' app.log
--color[=WHEN], --colour[=WHEN] use markers to highlight the matching strings; WHEN is 'always', 'never', or 'auto'
要点 :为匹配部分加颜色标记。WHEN
参数控制启用时机:always
总是启用,never
禁用,auto
在交互式终端时启用。
示例:
bash
grep --color=auto -n 'main' *.c
-U, --binary do not strip CR characters at EOL (MSDOS/Windows)
要点 :不要去掉行尾 \r
(在处理 CRLF 文件时保留回车字符),便于诊断 Windows 格式换行问题。
示例:
bash
grep -U '$' windows.txt | cat -A # 显示 ^M 等
输入 / 默认行为 / 退出状态
When FILE is '-', read standard input.
要点 :将文件名指定为 -
表示从标准输入读取数据。
示例:
bash
echo "hello" | grep 'h' - # "-" 表示 stdin
With no FILE, read '.' if recursive, '-' otherwise.
要点 :未指定文件名时,如果使用递归选项(如 -r
),grep
默认对当前目录 .
进行递归搜索;否则默认读取标准输入。
示例:
bash
grep -R 'pat' # 等同 grep -R 'pat' .
echo "abc" | grep 'a' # 无文件且不递归 → 读 stdin
With fewer than two FILEs, assume -h.
要点 :当输入的文件少于两个时(例如单个文件或标准输入),默认不打印文件名前缀,相当于 -h
。
示例:
bash
grep 'pattern' single_file.txt # 不显示文件名前缀
Exit status is 0 if any line is selected, 1 otherwise; if any error occurs and -q is not given, the exit status is 2.
要点:退出码含义:
0
:至少有一行匹配;1
:没有匹配;2
:发生错误(例如无法读取文件),除非使用-q
(静默)。
脚本中常用grep
的退出码做条件判断。
示例:
bash
if grep -q 'READY' status.txt; then echo OK; else echo NOT_READY; fi
附 --- 常见组合示例
- 在代码目录中递归查找包含
alloc
的.c/.h
文件,排除.git
与build
:
bash
grep -Rni --include='*.{c,h,cpp}' --exclude-dir={.git,build} 'alloc' .
- 统计项目中每个文件的
ERROR
出现次数:
bash
grep -Rc 'ERROR' logs/
- 从标准输入抽取所有 URL:
bash
curl -s http://example.com | grep -Eo 'https?://[^ ]+'
- 找出没有版权声明的源文件:
bash
grep -RL 'Copyright' src/
- 用 NUL 安全方式查找并处理文件名(与 xargs -0 配合):
bash
grep -Zl 'needle' -R . | xargs -0 -I{} echo "FOUND: {}"
- 只查找以数字 ID 出现的位置(PCRE 断言):
bash
grep -Po '(?<=ID=)\d+' data.txt
用法示例
看完能独立写出常用 90% 场景的 grep 命令。
A. 基础检索
bash
# 在两个文件里找 hello world(忽略大小写)
grep -i 'hello world' menu.h main.c
-i
:大小写不敏感。找到了就打印整行。
bash
# 在当前目录递归找包含 "TODO" 的行,并显示 文件名:行号:内容
grep -Rni --include='*.{c,h,cpp}' 'TODO' .
-R
:递归且跟随链接;-n
:行号;-H
默认在多文件时会加文件名;--include
缩小范围以提速。
B. 选择合适的模式类型
bash
# ERE:函数名(带括号),更符合直觉
grep -REn 'my_func\(' src/
- 在 ERE 下
(
不是特殊字符,但\(
可读性更好,避免歧义;若在 BRE(默认-G
)里则 必须 写\(
。
bash
# 纯文本多关键词(最快):把关键词写到文件里,每行一个
grep -Ff keywords.txt -R --include='*.log' -n .
bash
# PCRE:用词边界和后行断言抽取 ID
grep -aPo '(?<=ID=)\d+' big.bin
-a
把潜在二进制当文本;-P
支持断言;-o
只输出匹配片段(纯数字 ID)。
C. 输出控制(文件名、计数、仅匹配)
bash
# 只列出含有匹配的文件名
grep -Rl 'panic' /var/log
# 只列出没有匹配的文件名(比如找"缺少版权头"的源文件)
grep -RL 'Copyright .* MyCorp' src/
# 只统计每个文件匹配行数
grep -Rc 'ERROR' logs/
# 只输出匹配到的关键词,便于统计频次
grep -Rho --include='*.py' -E 'TODO|FIXME' . | sort | uniq -c | sort -nr
D. 反选与上下文
bash
# 反选:输出不含 DEBUG 的行
grep -Rnv --include='*.log' 'DEBUG' logs/
# 上下文:匹配行前后各 3 行
grep -RniC3 'OutOfMemoryError' logs/
# 或写成:grep -Rni -A3 -B3 'OutOfMemoryError' logs/
E. 性能与停止条件
bash
# 每个文件匹配到第一行就停(定位"是否存在")
grep -m1 -R 'needle' .
# 大量固定字符串,极致提速
LC_ALL=C grep -F --include='*.txt' -R -n -e 'foo' -e 'bar' -e 'baz' .
F. 二进制与 NUL 安全流水线
bash
# 把二进制当文本搜
grep -aR 'PNG' .
# 和 find/xargs 进行 NUL 安全联动,只输出匹配的文件名(以 NUL 结尾)
find . -type f -name '*.txt' -print0 \
| xargs -0 grep -Zl 'needle' \
| tr '\0' '\n' # 仅为了人眼阅读
G. 目录/设备处理与排除
bash
# 跳过特定目录,避免巨慢
grep -Rni --exclude-dir={.git,node_modules,dist,build} 'register' .
# 处理目录/设备的策略
grep -D skip -d recurse -R 'pattern' .
-d/--directories
:遇到目录 read/recurse/skip-D/--devices
:遇到设备/FIFO/socket read/skip
H. 脚本里更健壮
bash
# 静默测试(只看退出码)
if grep -qE '^(enabled|on|true)$' config.txt; then
echo "feature enabled"
fi
-q
静默;-E
用扩展正则;配合退出码0/1/2
做逻辑分支。