【Linux】通俗易懂讲解-正则表达式
- 博主简介:努力学习的22级计算机科学与技术本科生一枚🌸
- 博主主页: @Yaoyao2024
- 往期回顾:【科研小白系列】这些基础linux命令,你都掌握了嘛?
- 每日一言🌼: 永远不要只看见前方路途遥远 而忘了从前的自己坚持了多久才走到这里。🌺
核心概念一句话:
正则表达式(Regular Expression,简称 regex)就像文本的“超级搜索/匹配密码”。它用一组特殊符号和规则,描述你想要的文本“模式”,帮你快速查找、提取或修改复杂的字符串。
一、为什么需要正则?
- 场景1: 在1000行的日志文件里,找出所有包含
error
或ERROR
或Error
的行(大小写不敏感)。 - 场景2: 从一堆文件名中,筛选出所有以
.jpg
或.png
结尾的图片文件。 - 场景3: 把文件里所有手机号码
138xxxx1234
替换成[隐私保护]
。 - 场景4: 验证用户输入的邮箱地址
xxx@yyy.com
格式是否正确。
没有正则: 你可能需要写很复杂的循环和判断,或者肉眼一条条找,效率极低且易错。
有了正则: 一行命令或表达式就能搞定!这就是它的魔力。就像一个筛黄豆的筛子,只有符合预先设定“孔”的大小的黄豆才能通过!把“黄豆”直接放到筛子里就可以得到想要大小的黄豆。
二、正则表达式基础:认识“通配符”的升级版
Linux中你用过 *
(匹配任意字符) 吗?
正则就是更强大、更精确的“通配符”系统。它分为两类:
- 基本正则表达式 (BRE): 语法较老,某些元字符需要转义才能发挥特殊作用。常用工具如
grep
,sed
(默认模式),vi/vim
。 - 扩展正则表达式 (ERE): 语法更现代直观,元字符功能默认开启。常用工具如
grep -E
/egrep
,sed -E
,awk
。
小白建议: 先重点学习扩展正则表达式 (ERE),它更常用、更易读! 本文主要讲解 ERE。
三、核心元件:构建你的“匹配密码”
想象你在用乐高积木搭一个“文本模具”,只有符合这个模具形状的文本才能被匹配。
1. 普通字符
- 是什么: 字母 (a-z, A-Z)、数字 (0-9)、大部分标点符号。
- 作用: 精确匹配它们自己。
- 例子:
cat
匹配字符串"cat"
。
2. 元字符 - 超级英雄 (特殊含义符号)
这些是正则的核心力量!必须记住:
-
.
(点号):- 作用: 匹配任意一个字符(除了换行符
\n
)。 - 例子:
c.t
匹配"cat"
,"cot"
,"c@t"
,"c t"
等。 - 类比: 扑克牌里的“百搭牌”。
- 作用: 匹配任意一个字符(除了换行符
-
*
(星号):- 作用: 匹配前面的字符或子表达式 0次 或 多次。
- 例子:
ab*c
:匹配"ac"
(b出现0次),"abc"
(b出现1次),"abbc"
(b出现2次),"abbbbc"
(b出现多次)。.*
:超级常用! 匹配 任意长度(包括0长度)的任意字符序列(除了换行符)。相当于“什么都行,或者什么都没有”。
- 类比: “前面的东西可以重复出现很多次,或者干脆不出现”。
-
+
(加号):- 作用: 匹配前面的字符或子表达式 1次 或 多次。 (比
*
严格一点,至少出现1次) - 例子:
ab+c
匹配"abc"
,"abbc"
,"abbbbc"
,但不匹配"ac"
(因为b至少要有1个)。 - 类比: “前面的东西必须至少出现一次,可以出现很多次”。
- 作用: 匹配前面的字符或子表达式 1次 或 多次。 (比
这个
*
和+
总是成对出现(至少在我学过的离散数学和编译原理中),在编译原理这门课里面,我记得这两个符号表达的含义分别是“星推导”和“加推导”,作用和含义和上面解释的含义是一致的。
-
?
(问号):- 作用:
- 匹配前面的字符或子表达式 0次 或 1次。(表示可选)
- 在限定符 (
*
,+
,?
,{}
) 后面使它们变成 非贪婪匹配 (稍后详解)。
- 例子(作用1):
colou?r
匹配"color"
(u出现0次) 和"colour"
(u出现1次)。 - 类比: “前面的东西可有可无,最多出现一次”。
- 作用:
-
[]
(方括号 - 字符组):- 作用: 匹配方括号内列出的任意一个字符。
- 例子:
[aeiou]
:匹配任意一个小写元音字母 (a, e, i, o, u)。[0-9]
:匹配任意一个数字 (等价于\d
,但\d
不一定在所有工具中都支持,[0-9]
更通用)。[a-zA-Z]
:匹配任意一个字母(大小写)。[^abc]
:^
在开头表示否定!匹配除了 a, b, c 之外的任意一个字符。
- 重要: 在
[]
内,大部分元字符 (如.
,*
,+
) 会失去特殊含义,只代表它们自己。但-
(定义范围如a-z
),^
(开头表示否定),]
(结束符) 和\
(转义符) 在[]
内仍有特殊含义。 - 类比: “从这个小菜单里选一个字符”。
-
|
(竖线 - 或):- 作用: 匹配
|
左边或右边的表达式。 - 例子:
cat|dog
匹配字符串"cat"
或"dog"
。(gray|grey)
匹配"gray"
或"grey"
(通常和括号()
一起用)。 - 类比: “这个或者那个,二选一”。
- 作用: 匹配
-
()
(圆括号 - 分组):- 作用:
- 分组: 将多个字符组合成一个子表达式,以便应用限定符 (
*
,+
,?
,{}
) 或|
。 - 捕获: 匹配的内容会被“记住”,可以在替换操作 (
sed
, 编程语言中) 或模式后向引用 (\1
,\2
等) 中使用。
- 分组: 将多个字符组合成一个子表达式,以便应用限定符 (
- 例子:
(abc)+
:匹配"abc"
,"abcabc"
,"abcabcabc"
等。(ab|cd)ef
:匹配"abef"
或"cdef"
。
- 类比: “把这几块积木粘在一起当成一个整体来处理,并且记住这个整体长什么样”。
- 作用:
-
{}
(花括号 - 量词/重复次数):- 作用: 精确指定前面字符或子表达式出现的次数。
- 格式:
{n}
:精确匹配 n 次。{n,}
:匹配至少 n 次。{n,m}
:匹配至少 n 次,至多 m 次。
- 例子:
a{3}
:匹配"aaa"
。a{2,4}
:匹配"aa"
,"aaa"
,"aaaa"
。[0-9]{4}
:匹配任意连续的4位数字(如年份、部分电话号码)。
- 类比: “前面的东西必须精确出现/最少出现/最多出现多少次”。
-
^
(脱字符) 和$
(美元符):- 作用: 匹配位置,而不是具体字符。
^
:匹配一行的开头。$
:匹配一行的结尾。
- 例子:
^Hello
:匹配所有以"Hello"
开头的行。world$
:匹配所有以"world"
结尾的行。^$
:匹配空行 (开头紧跟着结尾)。^[A-Z]
:匹配所有以大写字母开头的行。
- 类比:
^
是“行首的锚点”,$
是“行尾的锚点”。用来把匹配“钉”在行的特定位置。
- 作用: 匹配位置,而不是具体字符。
-
\
(反斜杠 - 转义符):- 作用:
- 开启元字符的特殊含义: 在 BRE 中,要使
{
,}
,(
,)
,+
,?
,|
具有特殊含义,有时需要在它们前面加\
(在 ERE 中通常不需要)。 - 关闭元字符的特殊含义: 如果一个元字符 (如
.
,*
,?
) 你想让它表示普通字符本身(比如你想匹配一个实际的点号.
),就在它前面加\
。 - 引入特殊序列: 后面介绍。
- 开启元字符的特殊含义: 在 BRE 中,要使
- 例子:
my\.domain\.com
:匹配"my.domain.com"
(这里的.
就是普通的点号)。\*
:匹配一个实际的星号字符*
。\\
:匹配一个实际的反斜杠字符\
。
- 类比: “魔法开关”。给普通字符加魔法,或者去掉元字符的魔法让它变普通。
- 作用:
3. 预定义字符类 (便捷写法)
这些是常用字符组的缩写:
\d
: 匹配一个数字字符。等价于[0-9]
。 (注意:在基本正则grep/sed
中可能不支持,用[0-9]
更安全)\D
: 匹配一个非数字字符。等价于[^0-9]
。\w
: 匹配一个单词字符 (字母、数字、下划线_
)。等价于[a-zA-Z0-9_]
。(注意同上)\W
: 匹配一个非单词字符。等价于[^a-zA-Z0-9_]
。\s
: 匹配一个空白字符 (空格、制表符\t
、换行符\n
、回车符\r
等)。(注意同上)\S
: 匹配一个非空白字符。\b
: 匹配一个单词边界(单词的开头或结尾,即\w
和\W
之间的位置)。例如\bcat\b
匹配独立的单词"cat"
,不会匹配"catalog"
或"scatter"
中的"cat"
。
小白提示: 在 Linux 命令行工具 (grep
, sed
, awk
) 中,\d
, \w
, \s
等 不一定默认支持 (尤其在 BRE 模式)。[0-9]
, [a-zA-Z]
, [[:space:]]
是更可靠的选择。在编程语言 (Python, Perl, JavaScript) 中通常支持良好。
4. 贪婪匹配 vs. 非贪婪匹配 (重要概念!)
- 贪婪匹配 (Greedy): 默认行为!限定符 (
*
,+
,?
,{}
) 会尽可能多地匹配字符。 - 非贪婪匹配 (Lazy/Reluctant): 在限定符后面加上
?
,使其尽可能少地匹配字符。
例子解析:
假设文本是: "<em>重要内容</em> 和 <em>其他内容</em>"
- 贪婪模式:
/<em>.*<\/em>/
- 匹配结果:
"<em>重要内容</em> 和 <em>其他内容</em>"
(整个字符串,从第一个<em>
到最后一个</em>
,因为它“贪婪”地吃掉了中间的所有字符,包括第二个<em>
和</em>
)
- 匹配结果:
- 非贪婪模式:
/<em>.*?<\/em>/
- 匹配结果:
"<em>重要内容</em>"
(只匹配到第一个</em>
就停止了,因为它“不贪心”) - 如果需要匹配所有,通常配合全局匹配标志使用(在工具或编程语言中)。
- 匹配结果:
在 Linux 工具中: sed
和 awk
默认贪婪。grep -P
(Perl 正则,支持非贪婪 *?
) 或 sed
结合其他技巧可以实现非贪婪,但在基础命令行中有时需要更精确地写表达式来避免贪婪问题。.*?
在 grep -P
或 perl
/python
脚本中有效。
四、在 Linux 命令行中使用正则 (实战!)
最常用的三个工具:grep
, sed
, awk
。这里重点看 grep
和 sed
的基础正则应用。
1. grep
- 文本搜索利器
- 作用: 在文件或输入中搜索包含匹配正则模式的行。
- 常用选项:
-i
:忽略大小写。-v
:反向匹配,显示不包含模式的行。-E
:使用扩展正则表达式 (ERE) (推荐!)。-P
:使用 Perl 正则 (功能最强,支持非贪婪*?
等,但非所有系统默认支持)。-o
:只输出匹配到的部分 (而不是整行)。-n
:显示匹配行的行号。-r
/-R
:递归搜索目录下的文件。
- 例子:
# 在 file.txt 中查找包含 "error" (忽略大小写) 的行 grep -i 'error' file.txt# 使用 ERE 查找包含 "error" 或 "warning" 的行 grep -E 'error|warning' file.txt# 查找以数字开头、以句点结束的行 grep -E '^[0-9].*\.$' file.txt# 查找所有格式为 xxx-xxx-xxxx 的电话号码 (简单示例) grep -E '[0-9]{3}-[0-9]{3}-[0-9]{4}' contacts.txt# 查找包含 "functionName(" 的行 (匹配函数调用) grep 'functionName\(' code.js # 注意转义 ( (BRE 可能需要)# 递归搜索当前目录及子目录的 .py 文件,查找 'import os' 行 grep -r --include='*.py' 'import os' .
2. sed
- 流编辑器,文本替换大师
- 作用: 读取输入流,按行处理(查找、替换、删除、插入等),输出结果。非常适合基于正则的文本替换!
- 常用选项:
-E
:使用扩展正则表达式 (ERE) (推荐!)。-i
:原地编辑文件 (小心使用!建议先不加-i
测试)。-i.bak
先备份原文件。
- 核心命令:
s/pattern/replacement/flags
s
:表示替换 (substitute)。pattern
:要查找的正则表达式。replacement
:替换成的文本。flags
:标志位,常用:g
:全局替换 (一行中的所有匹配),不加则只替换每行第一个匹配。p
:打印替换后的行 (通常与-n
选项一起用)。
- 例子:
# 将 file.txt 中所有的 "colour" 替换成 "color" (标准输出,不修改文件) sed 's/colour/color/g' file.txt# 使用 ERE 将每行第一个数字替换成 "NUMBER" (原地编辑,先备份) sed -i.bak -E 's/[0-9]+/NUMBER/' data.txt# 删除所有空白行 (标准输出) sed '/^$/d' file.txt # /pattern/d 命令删除匹配行# 将手机号 138xxxx5678 替换成 [隐私保护] (简单示例) sed -E 's/1[3-9][0-9]{9}/[隐私保护]/g' users.txt# 在包含 "START" 的行后面插入一行 "--BEGIN DATA--" (标准输出) sed '/START/a --BEGIN DATA--' file.txt
awk
也是一个强大的文本处理工具,内置对正则的支持非常好,功能远超 grep
和 sed
(可以编程),但学习曲线更陡峭。建议掌握 grep
和 sed
基础后再学 awk
。
五、用例积累
主要用于积累博主平时用到的一些正则例子
- 去掉文件后缀,获得文件名,将其文件名作为一个list输入的指定文件
# 提取 /data1/share/te5/af3_json 中的 pdb-id(去掉 .json 后缀)
ls /data1/share/te5/af3_json| sed 's/\.json$//' |sort > list.txt
- 但当通过管道(|)输出到其他命令时,ls 会自动切换为 “每行一个文件名” 的格式(sed是按行处理)。
sed 's/\.json$//'
用于移除文件名末尾的 .json。