grep 命令详解与参数实测(Ubuntu 22.04)
grep 命令逐参数实测详解:匹配器选择、输出控制、上下文控制、目录递归过滤、二进制处理与退出码规则,全部命令与回显在 Ubuntu 22.04(GNU grep 3.7)实测通过。
grep 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU grep 的每一个常用参数——匹配器选择、输出控制、上下文控制、目录递归与二进制处理,全部命令与回显已在 Ubuntu 22.04(GNU grep 3.7)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- grep 版本:GNU grep 3.7(
grep --version实测) - 实测工作目录:
/root/grepdemo/ - 测试数据:
app.log(8 行模拟服务日志)、words.txt、nums.txt、patterns.txt、src/(3 个源码文件 + 1 个软链)、blob.bin(二进制样例)
二、命令概览
2.1 语法与测试数据准备
grep 的基本语法为 grep [OPTION]... PATTERNS [FILE]...:在指定 FILE(或标准输入)中搜索匹配 PATTERNS 的行并输出。测试前先准备一套演示文件,后续所有参数实测都基于这套数据:
mkdir -p /root/grepdemo/src /root/grepdemo/logs
cat > /root/grepdemo/app.log <<'EOF'
2026-10-15 09:00:01 INFO Service started on port 8080
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:04 INFO Health check OK
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
2026-10-15 09:00:06 error lowercase message should match with -i
2026-10-15 09:00:07 DEBUG retry internal detail
2026-10-15 09:00:08 INFO Service ready
EOF
cat > /root/grepdemo/words.txt <<'EOF'
cat catalog
dog category
bird concatenate
Cat certificate
fish
catalog
EOF
cat > /root/grepdemo/nums.txt <<'EOF'
1 apple
20 banana
3 cherry
145 date
EOF
cat > /root/grepdemo/patterns.txt <<'EOF'
ERROR
WARN
EOF
再准备源码目录与二进制样例(printf 中的 \x00 生成真实的二进制字节):
cat > /root/grepdemo/src/main.c <<'EOF'
#include <stdio.h>
int main(void) {
/* TODO: refactor this block */
printf("hello\n");
return 0;
}
EOF
cat > /root/grepdemo/src/util.c <<'EOF'
/* TODO: add input validation */
int add(int a, int b) { return a + b; }
EOF
cat > /root/grepdemo/src/readme.md <<'EOF'
# Demo project
TODO list in markdown too
EOF
printf 'abc\x00\x01\x02ERRORbinary\n' > /root/grepdemo/blob.bin
ln -sf /root/grepdemo/app.log /root/grepdemo/src/latest.log
验证数据就绪:
$ ls -l /root/grepdemo /root/grepdemo/src
/root/grepdemo:
total 28
-rw-r--r-- 1 root root 437 Oct 15 05:24 app.log
-rw-r--r-- 1 root root 18 Oct 15 05:24 blob.bin
drwxr-xr-x 2 root root 4096 Oct 15 05:24 logs
-rw-r--r-- 1 root root 36 Oct 15 05:24 nums.txt
-rw-r--r-- 1 root root 11 Oct 15 05:24 patterns.txt
drwxr-xr-x 2 root root 4096 Oct 15 05:25 src
-rw-r--r-- 1 root root 71 Oct 15 05:24 words.txt
/root/grepdemo/src:
total 12
lrwxrwxrwx 1 root root 22 Oct 15 05:25 latest.log -> /root/grepdemo/app.log
-rw-r--r-- 1 root root 111 Oct 15 05:24 main.c
-rw-r--r-- 1 root root 41 Oct 15 05:24 readme.md
-rw-r--r-- 1 root root 73 Oct 15 05:24 util.c
2.2 --help 全量回显
grep --help 输出全部参数清单(实测原样保留,可作为参数速查):
Usage: grep [OPTION]... PATTERNS [FILE]...
Search for PATTERNS in each FILE.
Example: grep -i 'hello world' menu.h main.c
PATTERNS can contain multiple patterns separated by newlines.
Pattern selection and interpretation:
-E, --extended-regexp PATTERNS are extended regular expressions
-F, --fixed-strings PATTERNS are strings
-G, --basic-regexp PATTERNS are basic regular expressions
-P, --perl-regexp PATTERNS are Perl regular expressions
-e, --regexp=PATTERNS use PATTERNS for matching
-f, --file=FILE take PATTERNS from FILE
-i, --ignore-case ignore case distinctions in patterns and data
--no-ignore-case do not ignore case distinctions (default)
-w, --word-regexp match only whole words
-x, --line-regexp match only whole lines
-z, --null-data a data line ends in 0 byte, not newline
Miscellaneous:
-s, --no-messages suppress error messages
-v, --invert-match select non-matching lines
-V, --version display version information and exit
--help display this help text and exit
Output control:
-m, --max-count=NUM stop after NUM selected lines
-b, --byte-offset print the byte offset with output lines
-n, --line-number print line number with output lines
--line-buffered flush output on every line
-H, --with-filename print file name with output lines
-h, --no-filename suppress the file name prefix on output
--label=LABEL use LABEL as the standard input file name prefix
-o, --only-matching show only nonempty parts of lines that match
-q, --quiet, --silent suppress all normal output
--binary-files=TYPE assume that binary files are TYPE;
TYPE is 'binary', 'text', or 'without-match'
-a, --text equivalent to --binary-files=text
-I equivalent to --binary-files=without-match
-d, --directories=ACTION how to handle directories;
ACTION is 'read', 'recurse', or 'skip'
-D, --devices=ACTION how to handle devices, FIFOs and sockets;
ACTION is 'read' or 'skip'
-r, --recursive like --directories=recurse
-R, --dereference-recursive likewise, but follow all symlinks
--include=GLOB search only files that match GLOB (a file pattern)
--exclude=GLOB skip files that match GLOB
--exclude-from=FILE skip files that match any file pattern from FILE
--exclude-dir=GLOB skip directories that match GLOB
-L, --files-without-match print only names of FILEs with no selected lines
-l, --files-with-matches print only names of FILEs with selected lines
-c, --count print only a count of selected lines per FILE
-T, --initial-tab make tabs line up (if needed)
-Z, --null print 0 byte after FILE name
Context control:
-B, --before-context=NUM print NUM lines of leading context
-A, --after-context=NUM print NUM lines of trailing context
-C, --context=NUM print NUM lines of output context
-NUM same as --context=NUM
--group-separator=SEP print SEP on line between matches with context
--no-group-separator do not print separator for matches with context
--color[=WHEN],
--colour[=WHEN] use markers to highlight the matching strings;
WHEN is 'always', 'never', or 'auto'
-U, --binary do not strip CR characters at EOL (MSDOS/Windows)
When FILE is '-', read standard input. With no FILE, read '.' if
recursive, '-' otherwise. With fewer than two FILEs, assume -h.
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.
💡 说明:帮助末尾三行是 grep 的核心行为约定——无 FILE 时读标准输入;递归模式下无 FILE 时读当前目录
.;文件数少于 2 个时默认不显示文件名前缀(等价-h)。退出码约定见本文 3.8 节。
2.3 版本确认
$ grep --version | head -2
grep (GNU grep) 3.7
Copyright (C) 2021 Free Software Foundation, Inc.
三、参数逐项实测
3.1 基础匹配(不带参数)
直接跟一个模式串与文件名,grep 逐行扫描文件并输出包含该模式的所有行:
$ grep ERROR /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
匹配默认区分大小写且为子串匹配(第 6 行小写 error 未被命中)。模式串建议始终用单引号包裹,避免 shell 展开空格与特殊字符。
3.2 -n:输出行号
排查日志时定位行号,便于配合 less +N 或编辑器跳转:
$ grep -n ERROR /root/grepdemo/app.log
2:2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
5:2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
💡 提示:
-n输出格式为行号:内容;若同时有文件名前缀,顺序是文件名:行号:内容。
3.3 -i:忽略大小写
$ grep -i error /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
2026-10-15 09:00:06 error lowercase message should match with -i
对比 3.1,-i 让大写 ERROR 与小写 error 都被命中。GNU grep 还有 --no-ignore-case 显式关闭该行为(默认即区分大小写,一般无需手动指定)。
3.4 -v:反选(排除匹配行)
只输出不包含模式串的行,常用于过滤噪音:
$ grep -v INFO /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
2026-10-15 09:00:06 error lowercase message should match with -i
2026-10-15 09:00:07 DEBUG retry internal detail
💡 提示:
grep -v grep是进程管道里的经典用法——ps aux | grep sshd会把 grep 自身也列出来,追加| grep -v grep即可剔除。
3.5 -c:只输出匹配行数
$ grep -c ERROR /root/grepdemo/app.log
2
多个文件时按文件分别输出计数(格式 文件名:数量),见 3.24 的 -cI 示例。注意 -c 计的是匹配的行数而不是模式出现次数——一行内出现两次也算 1。
3.6 -q:静默模式(只看退出码)
不输出任何内容,仅通过退出码表达结果,是脚本条件判断的标准姿势:
$ grep -q ERROR /root/grepdemo/app.log; echo exit=$?
exit=0
$ grep -q NOSUCHPATTERN /root/grepdemo/app.log; echo exit=$?
exit=1
💡 提示:
-q找到第一个匹配即退出,比-c更快,且不受输出重定向影响。脚本写法如if grep -q '^Port 22' /etc/ssh/sshd_config; then ...。
3.7 -w:全词匹配
只匹配"独立的单词",避免子串误伤:
$ grep -w cat /root/grepdemo/words.txt
cat catalog
第 1 行行首的 cat 是独立单词所以命中;第 2 行 category、第 3 行 concatenate 中的 cat 只是子串,均未命中。注意第 1 行的第二个词 catalog 也不影响判断——grep 是行级匹配,该行存在独立单词 cat 即输出整行。
3.8 -x:整行匹配
只有整行完全等于模式串才算命中:
$ grep -x fish /root/grepdemo/words.txt
fish
只有第 5 行整行就是 fish 被命中;cat catalog 等行虽含 cat 但整行不等于模式,全部排除。
3.9 -E:扩展正则表达式(ERE)
启用 ERE 后可直接使用 + ? | () {n,m} 而无需反斜杠转义:
$ grep -E 'ERROR|WARN' /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
$ grep -E 'err(or)?' /root/grepdemo/app.log
2026-10-15 09:00:06 error lowercase message should match with -i
ERROR|WARN 用或逻辑一次匹配两种级别;err(or)? 中 (or)? 表示 or 出现 0 次或 1 次,因此匹配 error(第 6 行小写)而大写 ERROR 因大小写不匹配未被命中。
💡 技术拓展:grep 支持四种正则"流派",通过参数切换,能力与语法差异是日常最大的选型点——
流派参数 名称 语法要点 适用场景 -GBRE 基本正则 默认流派;`()+?{} 需加` 转义才生效-EERE 扩展正则 ()+?{}|直接书写;等价egrep日常首选,可读性最好 -PPCRE Perl 正则 支持 \d \s、前瞻后顾、非贪婪复杂提取(日志解析、词法匹配) -F固定字符串 完全关闭正则,字面量匹配 搜索含 .*等元字符的明文,最快本文示例按"默认 BRE 演示基础、
-E演示或逻辑、-P演示\d"组织;纯字面量搜索(如匹配 IP192.168.1.1)优先用-F,既避免转义出错又最快。
3.10 -F:固定字符串匹配(非正则、最快)
模式串中的所有字符按字面量处理:
$ grep -F 'db-primary:5432' /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
$ grep -F 'err(or)?' /root/grepdemo/app.log; echo exit=$?
exit=1
第一个匹配按字面量命中含冒号与连字符的主机地址;第二个演示说明 err(or)? 在 -F 下不再具有正则含义,文件里没有这个字面字符串,返回退出码 1。
3.11 -G:基本正则表达式(BRE,默认流派)
显式声明 BRE。BRE 中 or 分组需要转义的 \(\):
$ grep -G 'err\(or\)' /root/grepdemo/app.log; echo exit=$?
2026-10-15 09:00:06 error lowercase message should match with -i
exit=0
等价于 grep -E 'err(or)'。不带 -E 时 grep 就是 BRE,-G 主要用于覆盖前文参数或脚本中显式表达意图。
3.12 -P:Perl 兼容正则(PCRE)
支持 \d(数字)、\s(空白)等 Perl 简写:
$ grep -P '\d{2}:\d{2}:\d{2}\s+ERROR' /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
\d{2}:\d{2}:\d{2} 精确锚定"时分秒"时间戳,\s+ 匹配一个以上空白后再接 ERROR,比 BRE/ERE 更直观。注意 Ubuntu 22.04 的 GNU grep 3.7 编译了 PCRE 支持,个别精简发行版的 grep 不带 -P,使用前建议先小范围验证。
3.13 -e:指定多个模式
每个 -e 携带一个模式,多次出现等于"或"逻辑:
$ grep -e ERROR -e WARN /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
与 grep -E 'ERROR|WARN' 等效,但 -e 适合模式中含 - 开头字符串(如 -e --version 不会被当成参数)或模式由变量循环拼接的场景。
3.14 -f:从文件读取模式列表
$ grep -f /root/grepdemo/patterns.txt /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
patterns.txt 每行一个模式(ERROR、WARN),任意一行命中即输出。适合维护一份关键词黑名单/白名单,避免把几十个模式堆进命令行。
3.15 -o:只输出匹配到的部分
默认输出整行,-o 只输出命中片段(每片一行):
$ grep -o '[0-9]\{2\}' /root/grepdemo/nums.txt
20
14
$ grep -oE '[0-9]+' /root/grepdemo/nums.txt
1
20
3
145
第一个示例在 BRE 中 {2} 必须写成 \{2\} 才表示"恰好 2 次":1 因不足两位被跳过、20、145(先取前两位 14)被输出。第二个示例用 -oE 匹配连续数字串,逐个输出。
💡 提示:
-oE是"从文本中抽取字段"的利器,例如grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' access.log可从日志中抽取所有 IPv4 地址。
3.16 -m NUM:最多匹配 NUM 行后停止
$ grep -m 2 ERROR /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
本例文件中 ERROR 恰好 2 行所以与完整输出一致;当文件远大于 NUM 时,-m 找够即停、不再扫描后续内容,对超大日志抽样很有价值。-m 1 只取第一条命中,常配合管道 | head -1 使用。
3.17 -b:输出字节偏移量
$ grep -b apple /root/grepdemo/nums.txt
0:1 apple
0: 表示命中内容起始于文件第 0 字节。常与 -o 搭配定位二进制或超大文件中关键串的位置。
3.18 -H / -h:强制显示 / 隐藏文件名前缀
$ grep -H ERROR /root/grepdemo/app.log | head -3
/root/grepdemo/app.log:2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
/root/grepdemo/app.log:2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
$ grep -h ERROR /root/grepdemo/app.log /root/grepdemo/patterns.txt | head -4
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
ERROR
规则回顾:文件数少于 2 个时默认隐藏文件名(-H 强制显示),多于 1 个时默认显示(-h 强制隐藏)。-h 在把多个文件的命中行合并成纯内容流时很有用。
3.19 --label=LABEL:给标准输入指定文件名标签
$ echo 'hello ERROR' | grep -H --label=stdin.log ERROR
stdin.log:hello ERROR
管道输入默认没有文件名前缀,--label 为其指定显示名,让脚本的报错信息可追溯到"虚拟来源"。
3.20 -s:抑制错误消息
$ grep NOSUCHFILE /root/grepdemo/nofile.txt; echo exit=$?
grep: /root/grepdemo/nofile.txt: No such file or directory
exit=2
$ grep -s NOSUCHFILE /root/grepdemo/nofile.txt; echo exit=$?
exit=2
不带 -s 时错误信息直接打到 stderr;加 -s 后错误被隐藏但退出码仍为 2(发生了错误)。适合在脚本里扫描一批可能不存在的文件而不污染输出。
3.21 -l:只列出包含匹配的文件名
$ grep -l TODO /root/grepdemo/src/*
/root/grepdemo/src/main.c
/root/grepdemo/src/readme.md
/root/grepdemo/src/util.c
每个命中的文件只列一次名字,不再输出行内容。配合 $(grep -l ...) 做二次处理是常见套路,例如批量替换前先筛文件。
3.22 -L:只列出不包含匹配的文件名
$ grep -L ERROR /root/grepdemo/src/*
/root/grepdemo/src/main.c
/root/grepdemo/src/readme.md
/root/grepdemo/src/util.c
与 -l 相反,列出所有"一个命中都没有"的文件。实测源码目录三个文件都不含 ERROR,所以全部列出。适合反向检查"哪些文件缺了某项配置/声明"。
3.23 --color=WHEN:高亮匹配文本
$ grep -n --color=always ERROR /root/grepdemo/app.log | cat -v | head -2
^[[32m^[[K2^[[m^[[K^[[36m^[[K:^[[m^[[K2026-10-15 09:00:02 ^[[01;31m^[[KERROR^[[m^[[K Connection refused: db-primary:5432
^[[32m^[[K5^[[m^[[K^[[36m^[[K:^[[m^[[K2026-10-15 09:00:05 ^[[01;31m^[[KERROR^[[m^[[K Connection refused: db-primary:5432
--color=always 无条件输出 ANSI 色码(^[[01;31m 为红色加粗),cat -v 把不可见控制字符转成 ^[ 形式才看到这些色码。默认值 auto 只在输出到终端时上色,管道/重定向时自动关闭,因此写脚本不需要显式加 --color=never。
3.24 -T:对齐制表位
$ grep -Tn apple /root/grepdemo/nums.txt
1: 1 apple
-T 让行号/字节偏移后补足制表位对齐,使不同位数的行号纵向对齐,便于人眼扫读。
3.25 -A NUM:显示匹配行及其后 NUM 行(After)
$ grep -A 2 '09:00:02 ERROR' /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:04 INFO Health check OK
命中第 2 行后连带输出第 3、4 行,用于查看错误发生"之后"发生了什么(是否重试成功、是否有后续异常)。
3.26 -B NUM:显示匹配行及其前 NUM 行(Before)
$ grep -B 2 '09:00:05 ERROR' /root/grepdemo/app.log
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:04 INFO Health check OK
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
3.27 -C NUM 与 -NUM:前后各 NUM 行(Context)
$ grep -C 1 '09:00:03 WARN' /root/grepdemo/app.log
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:04 INFO Health check OK
$ grep -2 '09:00:03 WARN' /root/grepdemo/app.log | head -5
2026-10-15 09:00:01 INFO Service started on port 8080
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:04 INFO Health check OK
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
-2 是 -C 2 的短写法。多组匹配之间若上下文不重叠,grep 会插入 -- 组分隔行:
$ grep -C1 -e 'ERROR Connection' -e 'lowercase' /root/grepdemo/app.log
2026-10-15 09:00:01 INFO Service started on port 8080
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
--
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
2026-10-15 09:00:06 error lowercase message should match with -i
2026-10-15 09:00:07 DEBUG retry internal detail
💡 提示:
--分隔行在二次过滤时可能造成干扰,可用--no-group-separator关闭,或用--group-separator='===='自定义分隔符。
3.28 -r:递归搜索目录(不跟随符号链接)
$ grep -rn TODO /root/grepdemo/src
/root/grepdemo/src/util.c:1:/* TODO: add input validation */
/root/grepdemo/src/main.c:3: /* TODO: refactor this block */
/root/grepdemo/src/readme.md:2:TODO list in markdown too
-r 递归进入 src/ 逐文件搜索,输出带 文件名:行号: 前缀。
3.29 --include / --exclude / --exclude-dir:按名称过滤搜索范围
$ grep -rn --include='*.c' TODO /root/grepdemo/src
/root/grepdemo/src/util.c:1:/* TODO: add input validation */
/root/grepdemo/src/main.c:3: /* TODO: refactor this block */
$ grep -rn --exclude='*.md' TODO /root/grepdemo/src
/root/grepdemo/src/util.c:1:/* TODO: add input validation */
/root/grepdemo/src/main.c:3: /* TODO: refactor this block */
$ grep -rn --exclude-dir=logs TODO /root/grepdemo
/root/grepdemo/src/util.c:1:/* TODO: add input validation */
/root/grepdemo/src/main.c:3: /* TODO: refactor this block */
/root/grepdemo/src/readme.md:2:TODO list in markdown too
三个参数都接受 shell 通配(*.c、?、[abc]):--include='*.c' 只搜 C 源码、--exclude='*.md' 排除 Markdown、--exclude-dir=logs 在递归整个工程目录时跳过日志目录。--exclude-from=FILE 可把多个排除模式写进文件批量引用。
3.30 -R:递归且跟随符号链接(与 -r 的实测差异)
$ grep -rn ERROR /root/grepdemo/src
$ grep -Rn ERROR /root/grepdemo/src | head -4
/root/grepdemo/src/latest.log:2:2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
/root/grepdemo/src/latest.log:5:2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
src/latest.log 是指向 app.log 的符号链接:-r 不跟随所以零输出;-R 跟随所以从链接目标中搜出 2 行 ERROR。全系统级搜索(如 /var/log 中大量软链)用 -R,工程代码搜索用 -r 防止重复与死循环。
3.31 -a / -I / --binary-files:二进制文件处理
先看默认行为——grep 检测到二进制内容后只给一行提示,不输出具体内容:
$ grep ERROR /root/grepdemo/blob.bin; echo exit=$?
grep: /root/grepdemo/blob.bin: binary file matches
exit=0
-a(等价 --binary-files=text)把二进制当文本处理,强制输出命中内容:
$ grep -a ERROR /root/grepdemo/blob.bin
abc ERRORbinary
-I(等价 --binary-files=without-match)直接跳过二进制文件——注意计数场景下跳过的二进制仍占一行 0 计数:
$ grep -cI ERROR /root/grepdemo/* 2>/dev/null
/root/grepdemo/app.log:2
/root/grepdemo/blob.bin:0
/root/grepdemo/logs:0
/root/grepdemo/nums.txt:0
/root/grepdemo/patterns.txt:1
/root/grepdemo/src:0
/root/grepdemo/words.txt:0
💡 提示:混有二进制的大目录搜索时
grep -rnI 关键词 目录是黄金组合,避免满屏binary file matches噪音。
3.32 -d ACTION:目录处理动作
$ grep -d skip ERROR /root/grepdemo/src; echo exit=$?
exit=1
$ grep -d recurse ERROR /root/grepdemo/src | head -3
-d skip 遇到目录直接跳过(src/ 里无 ERROR,退出码 1);-d recurse 等价 -r。-d read 则尝试把目录当文件读(会报错),一般不用。
3.33 退出码规则(脚本判断依据)
grep 的退出码有三档,实测逐条验证:
$ grep ERROR /root/grepdemo/app.log >/dev/null; echo exit=$?
exit=0
$ grep WARN /root/grepdemo/app.log >/dev/null; echo exit=$?
exit=0
$ grep NOSUCH /root/grepdemo/app.log 2>/dev/null; echo exit=$?
exit=1
0:至少有一行被选中(匹配到);1:没有匹配行(不是错误);2:发生错误(如文件不存在,见 3.20)。
脚本里 grep ... && echo found || echo missing 依赖退出码 0/1 语义;注意 -q 不会把"错误"降级成 1,错误仍是 2。
3.34 管道组合:过滤进程列表
grep 最常见的场景是接在管道后做文本过滤:
$ ps aux | grep -w 'sleep' | head -2
root 1 0.0 0.0 2792 1580 ? Ss 05:20 0:00 sleep infinity
root 3157 0.0 0.0 3472 1812 ? S 05:25 0:00 grep -w sleep
grep -w sleep 用全词匹配避免把 sleeping 之类的子串也捞出来;第二行是 grep 进程自身(ps 抓取快照时它正在运行),要剔除可加 grep -v grep 或使用 grep -w '[s]leep' 技巧(正则 [s]leep 匹配 sleep 但 ps 快照里 grep [s]leep 自身的命令行不包含字面 [s]leep 的匹配形态)。
四、实战示例
4.1 日志排障:错误现场 + 上下文 + 计数
$ grep -C 5 'ERROR Connection refused' /root/grepdemo/app.log
2026-10-15 09:00:01 INFO Service started on port 8080
2026-10-15 09:00:02 ERROR Connection refused: db-primary:5432
2026-10-15 09:00:03 WARN Retry attempt 1 for db-primary
2026-10-15 09:00:04 INFO Health check OK
2026-10-15 09:00:05 ERROR Timeout after 30s calling db-replica:5432
2026-10-15 09:00:06 error lowercase message should match with -i
2026-10-15 09:00:07 DEBUG retry internal detail
$ grep -c ERROR /root/grepdemo/app.log
2
-C 5 拉出错误前后现场还原调用链,-c 快速统计错误总量,两者是日志排障的最小组合。
4.2 工程搜索:递归 + 过滤 + 只列文件
$ grep -rn --include='*.c' -e TODO -e FIXME /root/grepdemo/src
/root/grepdemo/src/util.c:1:/* TODO: add input validation */
/root/grepdemo/src/main.c:3: /* TODO: refactor this block */
$ grep -rln TODO /root/grepdemo/src | wc -l
3
第一个命令等价于 IDE 里的"在 C 文件中搜索遗留标记注释";第二个统计含遗留标记的文件数。
4.3 脚本健康检查:-q 退出码 + -w 全词
$ ps aux | grep -qw 'sleep' && echo 'sleep is running' || echo 'sleep is NOT running'
sleep is running
-q 保证零输出、-w 防止误匹配同名前缀进程、退出码驱动 &&/|| 分支——三件套是服务存活检查的标准写法。
4.4 从文本中抽取字段:-oE + 管道
$ grep -oE '[0-9]{1,3}' /root/grepdemo/nums.txt | sort -n | tr '\n' ' '
1 3 20 145
-oE 抽出所有数字片段后交给 sort -n 数值排序,tr 把换行合并成空格,构成一条完整的"抽取→排序→压平"流水线。
五、注意事项与常见问题
Q1:为什么 [0-9]{2} 不报错但也匹配不到?
BRE(默认流派)中 { 是普通字符,量词必须写 \{2\};改用 grep -E 或 grep -P 即可直接写 {2}(见 3.9 流派对比与 3.15 实测)。
Q2:模式串以 - 开头怎么办?
用 -e 指定模式:grep -e '--version' file,避免被解析为参数(见 3.13)。
Q3:脚本里 grep 总把自身进程列出来?
ps aux | grep xxx 的第二行命中来自 grep 进程自身,用 grep -v grep 剔除,或用 grep '[x]xx' 让 ps 快照里的命令行不匹配自身(见 3.34)。
Q4:错误信息 No such file or directory 怎么静默?
stderr 重定向 2>/dev/null 或参数 -s;注意两者都不改变退出码 2,脚本判断时需区分"无匹配(1)"与"文件错误(2)"(见 3.20、3.33)。
Q5:二进制文件只提示 binary file matches 拿不到内容?
加 -a 强制按文本输出;反向场景(目录里混有二进制)用 -I 跳过二进制(见 3.31)。
Q6:-c 计数和实际出现次数对不上?
-c 统计的是匹配的行数,一行内多次出现只算 1;要统计出现总次数用 grep -o 模式 文件 | wc -l。
Q7:超长日志全量扫描太慢?
优先 -F 固定字符串(跳过正则引擎);只看前几条时用 -m NUM 提前停止;可再配合 -q(找到即停)。
Q8:递归搜索时软链文件被跳过?
-r 不跟随符号链接,-R 才跟随(见 3.30 实测差异)。
六、参考链接
- GNU grep 官方手册:https://www.gnu.org/software/grep/manual/grep.html
- 本机帮助:
grep --help、man grep(Ubuntu 22.04 内置) - POSIX grep 规范:https://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html