主题
从0到1搞懂 grep
1. 一句话说清楚
grep(Global Regular Expression Print)是 Linux/Unix 系统中用正则表达式在文本中搜索匹配行并输出的命令行工具。
生活类比:grep 就像图书馆的「关键词检索系统」——你告诉它一个关键词(模式),它就能从成千上万本书(文件)中,把所有包含这个关键词的页面(行)精确地找出来摆在你面前。你不需要一本本翻书,检索系统帮你秒级定位。
2. 为什么需要它
2.1 它解决了什么痛点?
在 Linux 世界里,一切皆文件。日志文件、配置文件、代码文件……动辄成千上万行。如果没有 grep:
- 你需要用
cat或less打开文件,然后肉眼一行行地找——效率极低 - 你无法快速定位一个错误日志中
ERROR出现的位置 - 你没办法批量检查几百个文件中哪些包含某个配置项
有了 grep,一条命令、一秒钟就能完成上述所有工作。
2.2 适用场景
| 场景 | 示例 | 说明 |
|---|---|---|
| 日志分析 | grep "ERROR" app.log | 快速定位错误日志 |
| 代码搜索 | grep -rn "TODO" ./src/ | 递归查找代码中的待办事项 |
| 配置检查 | grep "port" /etc/nginx/nginx.conf | 查看配置文件中的端口设置 |
| 管道过滤 | ps aux | grep java | 过滤出与 Java 相关的进程 |
| 文件筛选 | grep -l "main" *.py | 找出哪些 Python 文件包含 main |
| 数据提取 | grep -oP '\d+\.\d+\.\d+\.\d+' access.log | 从日志中提取所有 IP 地址 |
2.3 什么时候不该用它?
- 结构化数据处理:如果你需要解析 JSON、CSV 等结构化数据,用
jq、awk、csvtool更合适 - 复杂的多行匹配:grep 是面向「行」的工具,跨行匹配能力有限(虽然
-P有一定支持),此时awk、sed或perl更强 - 大规模代码搜索:在超大代码库中,
ripgrep(rg)比 grep 快 10~40 倍,且默认尊重.gitignore - 需要替换文本:grep 只负责「搜」,不负责「替」,替换用
sed或awk
3. 核心概念与原理
3.1 grep 的工作流程
grep 的核心逻辑非常简单:逐行读入 → 正则匹配 → 输出匹配行。
图解说明:grep 从文件或管道接收输入,逐行读取,对每一行用指定的正则表达式进行匹配。匹配成功的行被输出到标准输出,失败的行被丢弃。处理完所有行后,根据是否有匹配返回退出码(0=有匹配,1=无匹配,2=出错)。
3.2 三种正则表达式引擎
grep 支持三种正则表达式风格,这是理解 grep 的核心中的核心:
text
┌─────────────────────────────────────────────────────────┐
│ grep 正则引擎体系 │
├──────────────┬──────────────┬───────────────────────────┤
│ BRE (默认) │ ERE (-E) │ PCRE (-P) │
│ 基本正则 │ 扩展正则 │ Perl 兼容正则 │
│ │ │ │
│ 特点: │ 特点: │ 特点: │
│ 元字符需要 │ 元字符不需 │ 支持前后断言 │
│ 反斜杠转义 │ 要转义 │ 非贪婪匹配 │
│ │ │ 命名捕获组 │
│ 调用方式: │ 调用方式: │ 调用方式: │
│ grep │ grep -E │ grep -P │
│ (默认) │ 或 egrep │ (需 libpcre) │
└──────────────┴──────────────┴───────────────────────────┘图解说明:BRE 是 grep 的默认模式,元字符如
(){}|等需要加\才能生效;ERE(grep -E)中这些元字符直接使用,写法更自然;PCRE(grep -P)功能最强大,支持零宽断言等高级特性,但不是所有系统都支持。
3.3 为什么这样设计?
- BRE 是历史遗产:Unix 早期为了向后兼容,默认用 BRE,元字符需要转义
- ERE 是实用主义的产物:写正则时加一堆
\太痛苦,ERE 让常用元字符免转义 - PCRE 是现代需求的结果:BRE/ERE 无法满足复杂匹配需求(如前向断言),引入 Perl 引擎解决
实际建议:日常使用优先用 grep -E(ERE),需要高级特性时用 grep -P(PCRE)。
4. 关键机制详解
4.1 基础匹配与常用选项
是什么:grep 的命令格式和核心选项,是一切用法的基石。
基本语法:
bash
grep [选项] '模式' [文件...]怎么工作:通过组合不同选项控制搜索行为:
| 选项 | 作用 | 记忆技巧 |
|---|---|---|
-i | 忽略大小写 | ignore case |
-v | 反向匹配(输出不匹配的行) | invert |
-n | 显示行号 | line number |
-c | 只输出匹配行数 | count |
-l | 只输出包含匹配的文件名 | file name |
-L | 只输出不包含匹配的文件名 | 大写的 l,反向 |
-r / -R | 递归搜索目录 | recursive |
-w | 全词匹配 | word |
-x | 全行匹配 | exact line |
-o | 只输出匹配的部分(非整行) | only matching |
-m N | 最多匹配 N 行后停止 | max count |
-A N | 显示匹配行后 N 行 | After |
-B N | 显示匹配行前 N 行 | Before |
-C N | 显示匹配行前后各 N 行 | Context |
-E | 使用扩展正则(ERE) | Extended |
-P | 使用 Perl 正则(PCRE) | Perl |
-F | 固定字符串匹配(不解析正则) | Fixed string |
--color | 高亮匹配部分 | 彩色输出 |
--include | 只搜索匹配模式的文件 | 包含 |
--exclude | 排除匹配模式的文件 | 排除 |
--exclude-dir | 排除某些目录 | 排除目录 |
示例:
bash
# 在日志中忽略大小写搜索 error,显示行号和前后各2行上下文
grep -inC 2 "error" /var/log/syslog
# 递归搜索当前目录下所有 .py 文件中的 "import os"
grep -rn --include="*.py" "import os" .
# 查找不包含 "DEBUG" 的行
grep -v "DEBUG" app.log注意事项:
-r和-R的区别:-R会跟随符号链接,-r不会(GNU grep 中)- 使用
--color=auto可以让输出在终端中高亮,但通过管道时自动关闭颜色
4.2 正则表达式实战
是什么:grep 的灵魂在于正则表达式,掌握正则 = 掌握 grep 的 80% 能力。
怎么工作:下表对比 BRE 和 ERE 中常见元字符的写法差异:
| 功能 | BRE(grep 默认) | ERE(grep -E) | 含义 |
|---|---|---|---|
| 任意单字符 | . | . | 匹配任意一个字符 |
| 行首 | ^ | ^ | 锚定行的开头 |
| 行尾 | $ | $ | 锚定行的结尾 |
| 零次或多次 | * | * | 前一个字符出现 0 次或多次 |
| 一次或多次 | \+ | + | 前一个字符出现 1 次或多次 |
| 零次或一次 | \? | ? | 前一个字符出现 0 次或 1 次 |
| 分组 | \( \) | ( ) | 捕获分组 |
| 或 | | | | | 匹配左或右的表达式 |
| 重复次数 | \{n,m\} | {n,m} | 前一个字符出现 n 到 m 次 |
| 字符类 | [abc] | [abc] | 匹配 a、b 或 c |
| 取反字符类 | [^abc] | [^abc] | 匹配非 a、b、c 的字符 |
| 词边界 | \b | \b | 匹配单词边界 |
示例:
bash
# BRE: 匹配以数字开头的行(需要转义 +)
grep '^\([0-9]\)\+' file.txt
# ERE: 同样的功能,写法更简洁
grep -E '^[0-9]+' file.txt
# ERE: 匹配邮箱地址
grep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' contacts.txt
# PCRE: 使用前向断言,匹配后面跟着 "world" 的 "hello"
grep -P 'hello(?=.*world)' file.txt
# PCRE: 非贪婪匹配,提取引号中的内容
grep -oP '".*?"' config.json注意事项:
- 最常见的坑:忘记 BRE 和 ERE 的转义差异!在 BRE 中
(是普通字符,\(才是分组;ERE 中正好相反 - 建议养成始终用
-E的习惯,写起来更直观 .匹配任意字符,如果你想匹配字面量的点号,要写\.
4.3 管道组合与多命令协作
是什么:grep 最强大的特性之一是与 Linux 管道(|)无缝配合,形成数据处理流水线。
怎么工作:
图解说明:Linux 管道将前一个命令的标准输出(stdout)连接到下一个命令的标准输入(stdin)。grep 在管道中充当「过滤器」角色,只让匹配的数据通过。
示例:
bash
# 经典组合:查找 Java 进程(排除 grep 自身)
ps aux | grep java | grep -v grep
# 更优雅的写法:用字符类避免匹配自身
ps aux | grep '[j]ava'
# 统计 nginx 访问日志中 404 错误的数量
cat access.log | grep "404" | wc -l
# 更高效的写法(无用的 cat):
grep -c "404" access.log
# 查找最近10条包含 ERROR 的日志
tail -1000 app.log | grep "ERROR" | tail -10
# 组合 find + grep:在特定目录下搜索
find /etc -name "*.conf" -exec grep -l "proxy" {} \;
# 多管道流水线:提取、过滤、排序、去重
grep -oP 'src="\K[^"]+' index.html | sort | uniq -c | sort -rn注意事项:
- 避免无用的 cat(Useless Use of Cat):
cat file | grep pattern应该写成grep pattern file grep -v grep是个常见的技巧,但用grep '[j]ava'的字符类写法更优雅- 管道中 grep 的退出码会影响
set -e的脚本——如果 grep 没有匹配到任何行,返回码为 1(非零),可能导致脚本中断
4.4 递归搜索与文件过滤
是什么:在目录树中批量搜索文件内容,是日常开发中最高频的 grep 用法。
怎么工作:grep -r 会遍历指定目录下的所有文件,对每个文件执行正则匹配。
示例:
bash
# 递归搜索当前目录下所有文件
grep -rn "TODO" .
# 只搜索 .go 文件
grep -rn --include="*.go" "func main" .
# 搜索多种文件类型
grep -rn --include="*.{js,ts,jsx,tsx}" "useState" ./src
# 排除 node_modules 和 .git 目录
grep -rn --exclude-dir={node_modules,.git,dist} "import" .
# 排除某些文件
grep -rn --exclude="*.min.js" --exclude="*.map" "function" .
# 组合使用:在 Go 项目中搜索,排除测试文件和 vendor
grep -rn --include="*.go" --exclude="*_test.go" --exclude-dir=vendor "panic" .注意事项:
- 对大型项目递归搜索时,一定要用
--exclude-dir排除node_modules、.git、vendor等目录,否则会非常慢 - 如果项目很大,强烈建议用
ripgrep(rg)替代,它默认尊重.gitignore,速度快 10~40 倍 --include和--exclude的模式是 glob 模式(不是正则),支持*和?通配符
4.5 退出码与脚本编程
是什么:grep 的退出码(exit code)在 Shell 脚本中用于条件判断,是写脚本必须理解的机制。
怎么工作:
| 退出码 | 含义 |
|---|---|
0 | 找到了至少一个匹配 |
1 | 没有找到任何匹配 |
2 | 发生了错误(如文件不存在、正则语法错误) |
示例:
bash
# 在 if 语句中使用 grep 判断
if grep -q "error" /var/log/syslog; then
echo "发现错误日志!"
# 发送告警...
fi
# -q(quiet)模式:不输出任何内容,只返回退出码
# 非常适合脚本中的条件判断
# 在 set -e 的脚本中安全使用 grep
set -e
# 错误写法:如果没匹配到,grep 返回 1,脚本会退出
# grep "pattern" file.txt
# 正确写法 1:用 || true 兜底
count=$(grep -c "pattern" file.txt || true)
# 正确写法 2:用 if 包裹
if grep -q "pattern" file.txt; then
echo "找到了"
else
echo "没找到"
fi
# 检查配置文件是否包含某个必需的配置项
check_config() {
local config_file="$1"
local required_key="$2"
if ! grep -qE "^\s*${required_key}\s*=" "$config_file"; then
echo "错误: 配置文件中缺少 ${required_key}" >&2
return 1
fi
}注意事项:
-q是脚本中的好朋友:不产生输出,仅靠退出码传递结果,效率高set -e下必须小心 grep:grep 未匹配时返回 1,会触发set -e终止脚本-c返回的是匹配行数(不是匹配次数),一行中有多个匹配也只算一行
5. 动手实践
实践 1:日志分析实战
前置准备:准备一个模拟的日志文件
bash
# 创建测试日志文件
cat > /tmp/app.log << 'EOF'
2026-03-26 08:00:01 [INFO] Application started on port 8080
2026-03-26 08:00:02 [DEBUG] Loading configuration from /etc/app/config.yaml
2026-03-26 08:00:03 [INFO] Connected to database at 192.168.1.100:3306
2026-03-26 08:01:15 [WARN] Slow query detected: 2.3s for SELECT * FROM users
2026-03-26 08:02:30 [ERROR] Failed to connect to Redis at 192.168.1.200:6379
2026-03-26 08:02:31 [ERROR] Cache service unavailable, falling back to database
2026-03-26 08:03:00 [INFO] Request from 10.0.0.15: GET /api/users - 200 OK (45ms)
2026-03-26 08:03:01 [INFO] Request from 10.0.0.22: POST /api/login - 200 OK (120ms)
2026-03-26 08:03:02 [WARN] Request from 10.0.0.33: GET /api/admin - 403 Forbidden
2026-03-26 08:03:03 [ERROR] Request from 10.0.0.44: GET /api/data - 500 Internal Server Error
2026-03-26 08:04:00 [INFO] Scheduled task [cleanup] started
2026-03-26 08:04:05 [INFO] Cleaned up 150 expired sessions
2026-03-26 08:05:00 [DEBUG] Health check: CPU 45%, Memory 62%, Disk 78%
2026-03-26 08:06:00 [ERROR] OutOfMemoryError: Java heap space
2026-03-26 08:06:01 [ERROR] Application crashed, initiating restart...
EOF运行步骤:
bash
# 1. 找出所有 ERROR 级别的日志
echo "=== 所有 ERROR 日志 ==="
grep "ERROR" /tmp/app.log
# 2. 统计各级别日志的数量
echo -e "\n=== 日志级别统计 ==="
for level in INFO DEBUG WARN ERROR; do
count=$(grep -c "\[$level\]" /tmp/app.log)
echo "$level: $count 条"
done
# 3. 提取所有 IP 地址并去重
echo -e "\n=== 出现的 IP 地址 ==="
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' /tmp/app.log | sort -u
# 4. 查找 HTTP 500 错误,并显示上下文
echo -e "\n=== HTTP 500 错误(含上下文)==="
grep -B 1 -A 1 "500" /tmp/app.log
# 5. 找出所有非 INFO 级别的日志(反向过滤 + 二次过滤)
echo -e "\n=== 非 INFO 和 DEBUG 的日志(异常日志)==="
grep -vE "\[(INFO|DEBUG)\]" /tmp/app.log
# 6. 提取请求的 URL 路径
echo -e "\n=== 请求路径 ==="
grep -oE '(GET|POST|PUT|DELETE) /[^ ]+' /tmp/app.log预期输出:
text
=== 所有 ERROR 日志 ===
2026-03-26 08:02:30 [ERROR] Failed to connect to Redis at 192.168.1.200:6379
2026-03-26 08:02:31 [ERROR] Cache service unavailable, falling back to database
2026-03-26 08:03:03 [ERROR] Request from 10.0.0.44: GET /api/data - 500 Internal Server Error
2026-03-26 08:06:00 [ERROR] OutOfMemoryError: Java heap space
2026-03-26 08:06:01 [ERROR] Application crashed, initiating restart...
=== 日志级别统计 ===
INFO: 6 条
DEBUG: 2 条
WARN: 2 条
ERROR: 5 条
=== 出现的 IP 地址 ===
10.0.0.15
10.0.0.22
10.0.0.33
10.0.0.44
192.168.1.100
192.168.1.200
=== 非 INFO 和 DEBUG 的日志(异常日志)===
2026-03-26 08:01:15 [WARN] Slow query detected: 2.3s for SELECT * FROM users
2026-03-26 08:02:30 [ERROR] Failed to connect to Redis at 192.168.1.200:6379
...解读:通过不同选项的组合(
-c统计、-o提取、-v反向、-E扩展正则),我们可以从一份日志中快速提取出各种维度的信息。
实践 2:代码库搜索实战
前置准备:任意一个有代码的项目目录
bash
# 创建一个模拟的小项目
mkdir -p /tmp/myproject/{src,test,docs}
cat > /tmp/myproject/src/main.py << 'EOF'
import os
import sys
# TODO: Add logging module
def main():
"""Main entry point"""
port = os.environ.get("PORT", 8080)
# FIXME: This should validate input
print(f"Starting server on port {port}")
if __name__ == "__main__":
main()
EOF
cat > /tmp/myproject/src/utils.py << 'EOF'
import re
# TODO: Add caching support
def validate_email(email):
"""Validate email address format"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
def parse_config(filepath):
"""Parse configuration file"""
# FIXME: Handle missing file gracefully
config = {}
with open(filepath) as f:
for line in f:
key, value = line.strip().split("=")
config[key] = value
return config
EOF
cat > /tmp/myproject/test/test_utils.py << 'EOF'
from src.utils import validate_email
# TODO: Add more test cases
def test_valid_email():
assert validate_email("user@example.com") == True
def test_invalid_email():
assert validate_email("invalid-email") == False
EOF运行步骤:
bash
cd /tmp/myproject
# 1. 查找所有 TODO 和 FIXME 注释
echo "=== TODO/FIXME 清单 ==="
grep -rnE "(TODO|FIXME)" . --include="*.py"
# 2. 查找所有函数定义
echo -e "\n=== 函数定义 ==="
grep -rnE "^def " . --include="*.py"
# 3. 查找所有 import 语句
echo -e "\n=== 导入语句 ==="
grep -rn "^import\|^from" . --include="*.py"
# 4. 只列出包含 TODO 的文件名
echo -e "\n=== 包含 TODO 的文件 ==="
grep -rl "TODO" . --include="*.py"
# 5. 统计每个文件的代码行数(排除空行和注释)
echo -e "\n=== 有效代码行数 ==="
for f in $(find . -name "*.py"); do
lines=$(grep -cvE '^\s*$|^\s*#' "$f")
echo "$f: $lines 行有效代码"
done预期输出:
text
=== TODO/FIXME 清单 ===
./src/main.py:3:# TODO: Add logging module
./src/main.py:7: # FIXME: This should validate input
./src/utils.py:2:# TODO: Add caching support
./src/utils.py:9: # FIXME: Handle missing file gracefully
./test/test_utils.py:2:# TODO: Add more test cases
=== 函数定义 ===
./src/main.py:4:def main():
./src/utils.py:3:def validate_email(email):
./src/utils.py:8:def parse_config(filepath):
./test/test_utils.py:3:def test_valid_email():
./test/test_utils.py:6:def test_invalid_email():6. 横向对比
6.1 文本搜索工具对比
| 特性 | GNU grep | ripgrep (rg) | ack | ag (Silver Searcher) | git grep |
|---|---|---|---|---|---|
| 开发语言 | C | Rust | Perl | C | C |
| 速度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 默认递归 | ❌ 需 -r | ✅ | ✅ | ✅ | ✅ |
| 尊重 .gitignore | ❌ | ✅ | ❌ | ✅ | ✅(仅 git 仓库) |
| Unicode 支持 | 部分 | ✅ 完善 | ✅ | ✅ | 部分 |
| 文件类型过滤 | --include | -t(内置类型) | 内置类型系统 | 内置类型系统 | ❌ |
| 跨行匹配 | -P 有限支持 | -U | ❌ | ❌ | ❌ |
| 智能大小写 | ❌ | -S | -i --smart-case | -S | ❌ |
| 搜索压缩文件 | zgrep | ❌ | ❌ | ❌ | ❌ |
| 系统预装 | ✅ 几乎所有系统 | ❌ 需安装 | ❌ 需安装 | ❌ 需安装 | ✅ 随 git |
| 适用场景 | 通用文本搜索 | 大型代码库 | 代码搜索 | 代码搜索 | Git 仓库搜索 |
6.2 选型建议
| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 服务器运维、日志分析 | grep | 系统自带,无需安装,功能足够 |
| 大型代码库日常开发 | ripgrep | 速度最快,默认配置最合理 |
| 中小型项目代码搜索 | ag 或 ack | 针对代码优化,开箱即用 |
| Git 仓库内搜索 | git grep | 可搜索历史提交,与 git 深度集成 |
| Shell 脚本编写 | grep | 可移植性最好,任何系统都有 |
| 需要替换功能 | sed | grep 只搜不替,sed 擅长替换 |
6.3 grep 家族内部对比
| 命令 | 等价写法 | 说明 |
|---|---|---|
grep | - | 默认使用 BRE(基本正则) |
egrep | grep -E | 使用 ERE(扩展正则),已弃用,建议用 grep -E |
fgrep | grep -F | 固定字符串匹配(不解析正则),已弃用,建议用 grep -F |
pgrep | - | 不是 grep 家族! 这是查找进程 ID 的命令 |
zgrep | - | 在压缩文件(.gz)中搜索 |
bzgrep | - | 在 bzip2 压缩文件中搜索 |
xzgrep | - | 在 xz 压缩文件中搜索 |
7. 常见陷阱与最佳实践
7.1 常见陷阱
🪤 陷阱 1:特殊字符未转义
bash
# 错误:想搜索字面量 "192.168.1.1",但 . 匹配任意字符
grep "192.168.1.1" log.txt
# 会匹配到 "192X168Y1Z1" 这样的内容
# 正确:转义点号,或使用 -F 固定字符串模式
grep "192\.168\.1\.1" log.txt
grep -F "192.168.1.1" log.txt🪤 陷阱 2:单引号 vs 双引号
bash
# 双引号中 $ 会被 Shell 解释
grep "price is $100" file.txt # ❌ $100 被解释为变量 $1 + "00"
# 用单引号保护正则表达式
grep 'price is $100' file.txt # ✅ $ 被原样传递给 grep
# 或者转义
grep "price is \$100" file.txt # ✅ 也可以🪤 陷阱 3:grep -c 不是匹配次数
bash
echo -e "aaa\nbbb\naaa bbb aaa" | grep -c "aaa"
# 输出 2(不是 3!)
# -c 统计的是「匹配的行数」,不是「匹配出现的次数」
# 如果要统计出现次数,用 -o + wc -l
echo -e "aaa\nbbb\naaa bbb aaa" | grep -o "aaa" | wc -l
# 输出 3🪤 陷阱 4:二进制文件匹配
bash
# grep 遇到二进制文件会输出 "Binary file xxx matches"
grep "pattern" binary_file
# 强制当作文本处理
grep -a "pattern" binary_file
# 或者直接跳过二进制文件(递归搜索时)
grep -rI "pattern" . # -I 等同于 --binary-files=without-match🪤 陷阱 5:ps | grep 匹配到自身
bash
ps aux | grep nginx
# 输出中会包含 "grep nginx" 这一行本身
# 解决方案 1:再加一个 grep -v
ps aux | grep nginx | grep -v grep
# 解决方案 2(更优雅):用字符类
ps aux | grep '[n]ginx'
# 字符类 [n] 匹配字母 n,但 "grep [n]ginx" 本身不包含字面量 "nginx"7.2 最佳实践
| 实践 | 说明 |
|---|---|
始终使用 grep -E | ERE 更直观,避免 BRE 转义的困扰 |
脚本中用 -q 做判断 | 不产生输出,效率高,代码整洁 |
用 --color=auto | 终端中高亮匹配,管道中自动关闭 |
| 递归搜索时排除无关目录 | --exclude-dir={.git,node_modules} |
搜索固定字符串用 -F | 比正则快得多,且避免特殊字符问题 |
| 单引号包裹模式 | 避免 Shell 对 $、!、` 等字符的解释 |
大文件用 -m 限制 | grep -m 10 找到 10 个匹配就停止,节省时间 |
用 \b 做全词匹配 | 或直接用 -w 选项,避免部分匹配 |
8. 进阶路线与资源
8.1 学习路径
text
入门 进阶 精通
│ │ │
├─ 基本选项 -i/-n/-v ├─ ERE 正则语法 ├─ PCRE 高级特性
├─ 简单文本搜索 ├─ 递归搜索 + 文件过滤 ├─ 零宽断言 / 命名组
├─ 管道过滤 ├─ 管道组合技巧 ├─ 性能调优
└─ grep 退出码 ├─ 脚本中的 grep ├─ ripgrep 等现代替代
└─ BRE/ERE 差异 └─ 正则引擎原理(NFA/DFA)8.2 推荐资源
| 类型 | 资源 | 说明 |
|---|---|---|
| 📖 官方文档 | man grep 或 info grep | 最权威的参考,随时可查 |
| 📖 GNU 官方手册 | GNU Grep Manual | 在线完整文档 |
| 📖 经典书籍 | 《精通正则表达式》(Mastering Regular Expressions) | Jeffrey Friedl 著,正则圣经 |
| 📖 经典书籍 | 《Linux 命令行与 Shell 脚本编程大全》 | 实战场景丰富 |
| 🌐 在线工具 | regex101.com | 在线调试正则表达式,实时匹配预览 |
| 🌐 对比参考 | beyondgrep.com | grep 系列工具功能对比 |
| 🌐 速查表 | grep cheatsheet | grep 选项和正则速查 |
| 🔧 替代工具 | ripgrep (rg) | 现代化的 grep 替代,推荐在大型项目中使用 |
8.3 常见面试 / 工作高频问题
grep、awk、sed 的区别是什么?各自适合什么场景?
- grep:搜索/过滤(找到包含某模式的行)
- sed:流式编辑(查找并替换、删除行等)
- awk:结构化文本处理(按列提取、计算、格式化输出)
BRE 和 ERE 有什么区别?
- 主要区别在元字符是否需要转义:BRE 中
+?|(){}需要加\才是元字符;ERE 中直接使用
- 主要区别在元字符是否需要转义:BRE 中
如何在不打开文件的情况下判断文件是否包含某个关键词?
grep -q "keyword" file && echo "存在" || echo "不存在"
如何递归搜索目录但排除某些文件类型?
grep -rn --exclude="*.log" --exclude-dir=".git" "pattern" .
grep 返回码在脚本中有什么用?
- 0=匹配成功、1=无匹配、2=出错,配合
if/&&/||做条件判断
- 0=匹配成功、1=无匹配、2=出错,配合
总结:grep 是 Linux 世界中最基础也最重要的文本搜索工具。掌握 grep = 掌握了在海量文本中快速定位信息的能力。从简单的字符串搜索到复杂的正则匹配,从单文件到递归目录,从交互式使用到脚本编程——grep 无处不在。建议日常使用
grep -E,大型项目切换到ripgrep,两者结合,文本搜索无忧。