Shell 正则表达式入门:grep、sed、awk 中的 pattern matching
本文最后更新于 20 天前,如有失效请评论区留言。

作为一个在 Linux 命令行里摸爬滚打过的人,太理解正则表达式这种”一看就头大,用起来真香”的技术了。这篇笔记算是我整理的一些心得,希望能帮你跨过正则的门槛。

前言:为什么我们离不开正则?

想象一下:你有一个包含几万行日志的文件,想找出里面所有的 error 或者 ERROR 开头的行,或者精准匹配一个格式像 2023-10-27 14:35:22 的时间戳。手动找?噩梦。

这就是 Regular Expression (正则表达式) 登场的时刻。你可以把它理解为一种模式匹配的”通用语言”。我们用一套特定的符号(元字符)来描述文本的”形状”(pattern),然后让 grepsedawk 这些工具去帮你寻找符合这个形状的文本。它不是独立工具,而是内嵌在各种工具里的”查找引擎”。学好正则,文本处理效率直接上一个台阶。

基础元字符 – 构成模式的”字母表”

正则表达式由普通字符(比如 a, b, 1)和元字符(有特殊含义的字符)组成。先来认识几个最基础的。

  • . : 匹配除换行符外的任意单个字符。比如 a.c 可以匹配 abc, a1c, a c
  • * : 匹配前面的元素零次或多次ab*c 可以匹配 ac(b出现0次), abc, abbc
  • + : 匹配前面的元素一次或多次(需要 -E-P)。ab+c 能匹配 abc, abbc,但不能匹配 ac
  • ? : 匹配前面的元素零次或一次colou?r 可以匹配 colorcolour
  • [] : 字符集合,匹配方括号中的任意一个字符。[0-9] 匹配所有数字,[a-zA-Z] 匹配所有字母。
  • ^ : 在 [] 内部开头表示取反[^0-9] 匹配非数字),在外部则匹配行的开头
  • $ : 匹配行的结尾end$ 匹配以 end 结尾的行。

记住这些就能开始构建简单模式了。匹配以 Error 开头的行:^Error。匹配包含数字的行:[0-9]

在 grep 中”捕捞”目标

grep 可能是你接触正则的第一个命令。核心工作就是从输入中筛选出匹配指定 pattern 的行

基础用法:BRE (Basic Regular Expression)

默认情况下 grep 使用基本正则(BRE)。一些元字符如 +, ?, {, }, (, ), | 需要用反斜杠转义才有特殊含义,这有点反直觉。

# 找出 log.txt 中包含 'error' 或 'ERROR' 的行(BRE)
grep 'error|ERROR' log.txt

# 匹配 'abbc' 这样 b 出现两次的字符串
grep 'ab{2}c' log.txt

扩展正则:-E (ERE) 更自由

-E(或者 egrep)启用扩展正则(ERE)。元字符使用更符合直觉,不需要大量转义。

# ERE 版本:匹配 'error' 或 'ERROR'
grep -E 'error|ERROR' log.txt

# 匹配 'abc' 出现一次或多次
grep -E '(abc)+' log.txt

# 匹配 'color' 或 'colour'
grep -E 'colou?r' colorfile.txt

Perl 正则:-P (PCRE) 的”黑科技”

-P 启用 Perl 兼容正则(PCRE)。功能最强大,支持非贪婪匹配零宽断言,性能通常也更好。

# 用 PCRE 匹配 IP 地址(简化模式)
grep -P 'd{1,3}.d{1,3}.d{1,3}.d{1,3}' access.log
# d 等价于 [0-9],这是 PCRE 的便利

小提示: 刚开始建议多用 -E(ERE),平衡功能和易用性。需要处理复杂模式时再考虑 -P(PCRE)。

用 sed 进行模式替换

sed 是”流编辑器”,最常用的功能就是替换s 命令)。语法:sed 's/原模式/替换字符串/标志' 文件,这里的”原模式”就是正则表达式。

# 将 'foo' 替换为 'bar'(每行只替换第一个匹配项)
sed 's/foo/bar/' file.txt

# 全局替换:标志 g
sed 's/foo/bar/g' file.txt

# 结合正则:将 'colour' 或 'color' 统一替换为 'color'
sed -E 's/colou?r/color/g' file.txt

# 捕获组与反向引用:交换两个单词的位置
echo "hello world" | sed -E 's/(bw+b) (bw+b)/2 1/'
# 输出:world hello

sed 默认也是 BRE,推荐用 -E 启用 ERE。

awk 中的 pattern matching

awk 是一门强大的文本处理语言,模式匹配可以作用于整个行,也可以作用于特定字段(列)。

# 打印包含 'error' 的行
awk '/error/ {print}' logfile.txt

# 打印第一列是数字的行
awk '$1 ~ /^[0-9]+$/ {print $1, $2}' data.txt

# 模式取反:打印不包含 'debug' 的行
awk '!/debug/ {print}' logfile.txt

# 逻辑运算:第一列大于100 且 第二列匹配 'pass'
awk '$1 > 100 && $2 ~ /pass/ {print}' results.txt

# match 函数 + RSTART/RLENGTH
echo "abc 123 def 456" | awk '{
    if (match($0, /[0-9]+/)) {
        printf "Found: %s at pos %dn", substr($0, RSTART, RLENGTH), RSTART
    }
}'
# 输出:Found: 123 at pos 5

awk 的正则支持 ERE,~(匹配)和 !~(不匹配)运算符用起来非常直观。

贪婪 vs 非贪婪匹配

默认情况下,正则是贪婪的(greedy)—— 尽可能多地匹配。比如 .* 会吞掉尽可能多的字符。

# 贪婪:匹配从 <b> 到最后一个 </b>
echo '<b>hello</b> world <b>foo</b>' | grep -oP '<b>.*</b>'
# 输出:<b>hello</b> world <b>foo</b>

# 非贪婪(lazy):加 ? 后缀,尽可能少匹配
echo '<b>hello</b> world <b>foo</b>' | grep -oP '<b>.*?</b>'
# 输出:
# <b>hello</b>
# <b>foo</b>

注意: 非贪婪匹配(*?, +?, ??)只在 PCRE(grep -P)中有效。BRE 和 ERE 不支持这个特性。

实用技巧和常见坑

  • 引号问题:在 shell 中使用正则时,单引号包裹 pattern 最安全。双引号会被 shell 解释 $, !, ` 等字符。
  • 特殊字符转义:要匹配 ., *, +, ?, [, ], (, ), {, }, ^, $, |, 这些字面量,用 转义。
  • b 词边界:PCRE 支持 b,匹配单词边界。比如 berrorb 不会匹配 errorsterror
  • 性能陷阱:避免 .*.* 这种嵌套通配符,容易导致 catastrophic backtracking(灾难性回溯)。
  • 调试方法:用 grep -oP 'pattern' file 只输出匹配的部分,方便调试 pattern 是否正确。

总结

正则表达式的学习曲线确实陡,但一旦掌握了基础元字符和 grep/sed/awk 三个工具的配合,你会发现自己处理文本的能力完全上了一个层次。建议从 grep -E 开始练手,遇到复杂需求再上 -P。多练,多踩坑,正则这东西就是熟能生巧。

版权声明:除特殊说明,博客文章均为原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇