Shell 的 grep 命令详解(新手详细版)
本文档面向零基础新手,从“grep 是干什么的”讲起,详细说明基本用法、常用选项、正则表达式、多文件与递归、实用示例等,并配有大量示例。
一、grep 是什么?
1.1 一句话理解
grep 用来在文本里按“模式”(字符串或正则)查找,把包含该模式的那一行整行打印出来。
- globally search for a regular expression and print(在全文里按正则搜索并打印)。
- 输入可以来自:文件、管道传过来的前一个命令的输出。
- 不修改文件,只做“查找并显示”。
1.2 典型用途
- 在日志里找报错、关键字;
- 在代码里找函数名、配置项;
- 在命令输出里筛出需要的行(配合管道
|)。
二、基本语法
grep [选项] 模式 [文件...]
- 模式:要搜的字符串或正则表达式(见后文)。
- 文件:一个或多个文件;不写则从标准输入读(常和管道一起用)。
示例:
# 在 file.txt 里找包含 "hello" 的行
grep "hello" file.txt
# 在多个文件里找
grep "error" log1.txt log2.txt
# 前一个命令的输出里找(标准输入)
cat file.txt | grep "hello"
ls -l | grep ".txt"
三、最常用选项(先记这些)
| 选项 | 含义 | 示例 |
|---|---|---|
-i |
忽略大小写 | grep -i "error" log.txt |
-n |
显示行号 | grep -n "hello" file.txt |
-v |
反向:只显示不包含模式的行 | grep -v "^#" config.conf |
-c |
只显示匹配行数,不显示内容 | grep -c "error" log.txt |
-l |
只显示有匹配的文件名,不显示具体行 | grep -l "TODO" *.c |
-L |
只显示没有匹配的文件名 | grep -L "TODO" *.c |
-r 或 -R |
递归在目录下所有文件中搜 | grep -r "function" ./src |
-w |
整词匹配(前后是“非单词字符”或边界) | grep -w "root" file.txt |
-x |
整行匹配(整行就是模式) | grep -x "admin" users.txt |
-q |
安静模式:只根据是否匹配设置退出码,不输出 | 脚本里用 if grep -q "ok" file; then ... |
可以组合:grep -in "error" log.txt(忽略大小写 + 显示行号)。
四、按“行前后”显示:-A、-B、-C
有时不仅想看匹配的那一行,还想看前后几行(如日志的上下文)。
| 选项 | 含义 |
|---|---|
-A n |
匹配行后面 n 行也显示(After) |
-B n |
匹配行前面 n 行也显示(Before) |
-C n |
匹配行前后各 n 行(Context) |
示例:
# 匹配行及后面 2 行
grep -A 2 "error" log.txt
# 匹配行及前面 2 行
grep -B 2 "error" log.txt
# 匹配行前后各 3 行
grep -C 3 "Exception" app.log
输出里会用 -- 分隔不同匹配块。
五、正则表达式(模式里能写什么)
grep 的模式默认是“基本正则”(BRE);加 -E 是“扩展正则”(ERE),写法更直观。下面先按“常用写法”讲,需要时说明 BRE/ERE 区别。
5.1 普通字符
大部分字符就是字面意思:hello 就匹配字母 h、e、l、l、o 连在一起。
grep "hello" file.txt
5.2 特殊字符与转义
以下字符在正则有特殊含义,要匹配字面时需转义(前面加 ):
| 字符 | 含义(正则) | 匹配字面写法 |
|---|---|---|
. |
任意一个字符 | . |
* |
前一个字符出现 0 次或多次 | * |
^ |
行首 | ^ |
$ |
行尾 | $ |
[ ] |
字符集合 | [、] |
|
转义符 | \ |
示例:
# 匹配 "file." 后面跟任意一个字符(如 file.txt、file1)
grep "file." file.txt
# 匹配以 "error" 开头的行
grep "^error" log.txt
# 匹配以 "end" 结尾的行
grep "end$" file.txt
# 匹配空行(行首紧跟行尾)
grep "^$" file.txt
5.3 点 . 和星号 *
.:匹配任意一个字符(除换行)。- *``(基本正则):前一个字符出现 0 次或多次**。
# 匹配 f 后跟任意两个字符,如 fit、fat、f12
grep "f.." file.txt
# 匹配 若干数字(0~9 出现 0 次或多次,要配合 [0-9])
# 基本正则里:grep "[0-9]*" 可能不是你想的“一整串数字”,见下节
5.4 字符类 [ ]
[abc]:匹配 a、b、c 中任意一个。[0-9]:一个数字;[a-z]:一个小写字母;[A-Z]:一个大写字母。[^abc]:除 a、b、c 外的任意一个字符(^ 在 [ ] 内表示“取反”)。
# 匹配包含数字的行
grep "[0-9]" file.txt
# 匹配以数字开头的行
grep "^[0-9]" file.txt
# 匹配非空行(行首不是换行,即至少有一个非空字符)
grep "." file.txt
# 或排除空行:
grep -v "^$" file.txt
5.5 扩展正则 -E:+、?、|、()
加 -E 后可用扩展正则,更易写:
| 写法 | 含义 |
|---|---|
+ |
前一个字符 1 次或多次 |
? |
前一个字符 0 次或 1 次 |
| |
或,如 a|b 表示 a 或 b |
() |
分组,如 (err|ERR) |
示例:
# 匹配 error 或 ERROR(-E 下 | 不需要转义)
grep -E "error|ERROR" log.txt
# 匹配 一个或多个数字
grep -E "[0-9]+" file.txt
# 匹配 可选前缀 + hello:如 hello、xhello
grep -E "x?hello" file.txt
注意:不加 -E 时,+、?、| 在基本正则里要么没有,要么要反斜杠(如 +、|),建议需要时直接用 grep -E。
5.6 词边界 -w(整词)
-w 要求模式是“整词”:前后必须是非字母数字下划线(或行首/行尾)。
# 只匹配独立单词 "root",不匹配 "rooter"、"myroot"
grep -w "root" /etc/passwd
5.7 只输出“匹配到的部分”:-o
-o 只打印匹配到的字符串本身,一行里多次匹配会多行输出。
# 只输出匹配到的邮箱(假设模式写对了)
grep -oE "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}" file.txt
六、多文件与递归
6.1 多个文件
在多个文件里搜,grep 会在每行前加上文件名(用 -h 可去掉文件名):
grep "error" log1.txt log2.txt log3.txt
# 输出示例:
# log1.txt:2024-01-01 10:00 error connection refused
# log2.txt:line 5: error ...
6.2 递归目录:-r、-R
在目录及其子目录下所有文件里搜索:
grep -r "function_name" ./src
grep -rn "TODO" . # -n 带行号
常用组合:
| 需求 | 命令示例 |
|---|---|
| 递归 + 行号 | grep -rn "pattern" 目录 |
| 递归 + 忽略大小写 | grep -ri "pattern" 目录 |
| 只显示文件名 | grep -rl "pattern" 目录 |
| 排除二进制(少误报) | grep -rI "pattern" 目录(-I 忽略二进制) |
6.3 排除/只搜某类文件(需配合 find 或 –include)
grep 自身没有“只搜 .c 文件”的选项,可配合 find:
# 只在 .c 和 .h 文件里搜
find . -name "*.c" -o -name "*.h" | xargs grep "main"
# 或
grep -r "main" --include="*.c" --include="*.h" .
–include、–exclude(GNU grep):
grep -r "config" --include="*.conf" .
grep -r "debug" --exclude="*.log" .
七、输入来源:文件 vs 标准输入
7.1 从文件读
grep "hello" file.txt
grep "hello" file1.txt file2.txt
7.2 从管道来(标准输入)
前一个命令的标准输出作为 grep 的输入:
cat file.txt | grep "hello"
ls -l | grep ".txt$"
ps aux | grep nginx
echo -e "anbnc" | grep "b"
注意:用管道时不要写文件名,写了文件名 grep 会去读文件,而不是管道的输出:
# 正确:grep 从管道读
cat file.txt | grep "hello"
# 错误理解:grep 不会从 cat 读,而是直接打开 file.txt(这里结果碰巧一样)
cat file.txt | grep "hello" file.txt
八、退出码(脚本里判断是否匹配)
- 匹配到至少一行:退出码 0。
- 没匹配到:退出码 1。
- 出错(如文件不存在):退出码 2。
配合 -q(不输出,只根据是否匹配设退出码):
if grep -q "success" result.txt; then
echo "找到了 success"
else
echo "没找到"
fi
九、彩色高亮:–color
让匹配到的字符串在终端里高亮(很多系统已默认打开):
grep --color=auto "error" log.txt
# 或
grep --color "error" log.txt
auto 表示“输出到终端就高亮,输出到文件或管道就不带颜色码”。
十、实用示例汇总
10.1 在日志里找错误
grep -i "error" /var/log/syslog
grep -in "error|exception|fail" app.log
grep -C 2 "error" app.log
10.2 排除注释和空行(看“有效配置”)
grep -v "^#" /etc/ssh/sshd_config | grep -v "^$"
10.3 在代码里找函数/字符串
grep -rn "main" --include="*.c" --include="*.h" .
grep -r "TODO|FIXME" ./src
10.4 看某用户/某进程
grep "zhangsan" /etc/passwd
ps aux | grep nginx
10.5 只统计出现次数
grep -c "error" log.txt
10.6 整词匹配,避免误匹配
grep -w "root" file.txt
10.7 静默判断(脚本里)
grep -q "ok" status.txt && echo "成功" || echo "失败"
10.8 匹配 IP(简单示例,不严谨)
grep -E "[0-9]+.[0-9]+.[0-9]+.[0-9]+" file.txt
十一、与 egrep、fgrep 的关系
- egrep:等价于 grep -E(扩展正则)。
- fgrep:等价于 grep -F(固定字符串,不把模式当正则,特殊字符不当元字符)。
建议:直接记 grep -E 和 grep -F,不用记 egrep/fgrep。
# 模式里有大量 . * 等,不想当正则,就当普通字符搜
grep -F "file.*.txt" list.txt
十二、常见注意点
- 模式里有空格或特殊字符:用引号包起来,如
grep "hello world" file.txt。 - 匹配反斜杠:要写
\,在引号里也可能要\\,视 shell 和引号而定。 - 二进制文件:grep 会尝试匹配二进制,可能刷屏乱码;用 -I(或 -binary-files=without-match)跳过二进制。
- 大文件/大量文件:递归时用 –include 缩小范围,避免搜到无关大文件。
十三、选项速查表
| 选项 | 含义 |
|---|---|
-i |
忽略大小写 |
-n |
显示行号 |
-v |
反向(不包含模式的行) |
-c |
只显示匹配行数 |
-l |
只显示有匹配的文件名 |
-L |
只显示无匹配的文件名 |
-r / -R |
递归目录 |
-w |
整词匹配 |
-x |
整行匹配 |
-A n |
匹配行后 n 行 |
-B n |
匹配行前 n 行 |
-C n |
匹配行前后各 n 行 |
-E |
扩展正则 |
-F |
固定字符串(不解释正则) |
-o |
只输出匹配到的部分 |
-q |
安静模式(只设退出码) |
-h |
多文件时不输出文件名 |
-H |
强制输出文件名 |
-I |
忽略二进制文件 |
--color=auto |
匹配高亮 |
--include=GLOB |
只搜匹配 GLOB 的文件 |
--exclude=GLOB |
排除匹配 GLOB 的文件 |
十四、小结
- grep 在文本中按模式查找,打印包含该模式的行;可来自文件或管道。
- 必会选项:
-i、-n、-v、-c、-l、-r、-w、-A/-B/-C、-E、-q。 - 正则:
.、*、^、$、[ ]、[^ ];用 -E 时可用+、?、|、()。 - 脚本里用 -q 配合退出码判断是否匹配;-l 看哪些文件有匹配;-r 递归目录。
多在实际文件里练几遍:单文件、多文件、管道、递归、-v 排除、-C 看上下文,很快就能熟练。若你已有 Linux文件与目录管理.md,可和其中的“在文件内容里搜索:grep”一节对照看。
文档以 GNU grep 为准;BSD/macOS 自带 grep 部分选项可能略有差异。