-
Linux 文本处理三剑客:正则表达式、sed 与 awk
在 Linux 系统的日常运维和开发工作中,文本处理是一项核心技能。无论是分析日志、配置文件,还是自动化脚本编写,我们都离不开强大的文本处理工具。
本文将带你深入理解正则表达式,并掌握sed和awk这两个被誉为 “文本处理三剑客” 的工具,让你在处理文本时游刃有余。
一、正则表达式:文本匹配的魔法
1. 什么是正则表达式?
正则表达式(Regular Expression,简称 regex),是一种用于描述、匹配一系列符合某个句法规则的字符串的方法。你可以把它想象成一个 “模板”,用来在海量的文本中快速找到你需要的内容。
对于普通计算机用户来说,可能很少直接接触到它,但对于系统管理员和开发者而言,正则表达式是必备的 “瑞士军刀”。它能帮你从纷繁复杂的日志中快速定位 “用户登录失败”、“服务启动错误” 等关键信息,也能让邮件服务器高效地过滤垃圾邮件。
2. 基础正则表达式:从简单开始
基础正则表达式是构建更复杂匹配规则的基石,我们先来认识几个最常用的元字符:
(1)锚定字符:定位行首与行尾
有时候我们不关心字符串在句子中间,而是想精确找到 “以什么开头” 或 “以什么结尾” 的行,这时候就需要用到锚定字符。
-
^:行首锚定符,用来匹配行的开始位置。
例如,要查找test.txt文件中所有以the开头的行,可以使用:
bash
grep -n '^the' test.txt
这条命令会输出所有行首是the的行,并显示行号。
-
$:行尾锚定符,用来匹配行的结束位置。例如,要查找所有以小数点.结尾的行,因为.在正则中是特殊字符,需要用反斜杠\\转义:
bash
grep -n '\\.$' test.txt
(2)任意字符与重复:灵活匹配
文本中充满了不确定性,我们需要元字符来处理这种 “模糊” 的情况。
-
.:点号,代表任意一个字符。例如,要查找所有以w开头,d结尾,中间夹着任意一个字符的单词(如wod、wdd、w2d),可以用:
bash
grep -n 'w.d' test.txt
它会匹配到像wood(这里oo被视为两个.的匹配)这样的字符串。
-
*:星号,代表前面的一个字符可以重复零次或多次。结合.使用,.*就成了 “匹配任意长度的任意字符” 的经典组合。例如,要查找所有以w开头,d结尾,中间可以有任意多个字符(包括没有字符)的行:
bash
grep -n 'w.*d' test.txt
这条命令会匹配wd、wood、woooood等所有符合条件的字符串。
(3)字符集合:精准筛选
当你需要匹配 “某一类” 字符,而不是一个具体的字符时,字符集合[]就派上用场了。
[abc]:匹配a、b或c中的任意一个字符。例如,要同时查找shirt和short这两个单词,它们都包含sh和rt,中间的字母是i或o:
bash
grep -n 'sh[io]rt' test.txt
这条命令会一次性匹配到这两个单词所在的行。
-
[a-z]:匹配所有小写英文字母。要查找所有以小写字母开头的行:
bash
grep -n '^[a-z]' test.txt
-
[^a-z]:注意,在[]内部的^是 “取反” 的意思,代表 “不匹配”。要查找所有不以小写字母开头的行:
bash
grep -n '^[^a-z]' test.txt
(4)限定连续字符范围:精确控制次数
*虽然好用,但它太 “自由” 了。如果你想精确控制一个字符出现的次数,比如 “恰好出现 2 次”、“至少出现 2 次”,就需要用到{}。
-
\\{n\\}:前面的字符恰好重复n次。例如,查找恰好包含两个连续o的字符串:
bash
grep -n 'o\\{2\\}' test.txt
-
\\{n,m\\}:前面的字符至少重复n次,最多重复m次。例如,查找以w开头,d结尾,中间包含 2 到 5 个o的字符串(如wood、woooood):
bash
grep -n 'wo\\{2,5\\}d' test.txt
-
\\{n,\\}:前面的字符至少重复n次。例如,查找以w开头,d结尾,中间包含 2 个或更多o的字符串:
bash
grep -n 'wo\\{2,\\}d' test.txt
⚠️ 注意:在基础正则表达式中,{和}是特殊字符,需要用反斜杠\\进行转义,写成\\{ \\}。
3. 扩展正则表达式:简化你的指令
基础正则表达式虽然强大,但在处理一些复杂逻辑时,命令会变得冗长。这时,我们可以使用扩展正则表达式(Extended Regular Expression, ERE),它在基础正则的基础上增加了几个元字符,让指令更简洁。要使用扩展正则,需要用egrep命令或者grep -E选项。
- +:匹配前面的字符一次或多次(等价于\\{1,\\})。
- ?:匹配前面的字符零次或一次(等价于\\{0,1\\})。
- |:表示 “或” 的逻辑,匹配|两边任意一个表达式。
例如,使用基础正则查找fish或fosh需要写成fis|osh,而使用扩展正则则更直观:
bash
egrep -n 'fi|os' test.txt
这条命令会同时匹配到包含fi或os的行。
二、sed:流编辑器,文本处理的自动化大师
如果说正则表达式是 “查找” 的利器,那么sed(Stream Editor)就是 “编辑” 的神器。它是一个非交互式的文本编辑器,可以对文本文件进行自动的删除、替换、添加、插入等操作,是编写 Shell 脚本自动化处理文本的首选工具。
1. sed 的工作原理
sed的工作流程非常清晰,主要分为三个步骤:
⚠️ 重要提示:默认情况下,sed不会修改原始文件,所有的修改都只发生在内存的模式空间中。如果你想直接修改源文件,需要使用-i选项。
2. 核心操作:p(打印)、d(删除)、s(替换)
sed的命令非常丰富,但最核心、最常用的就是以下三个操作:
(1)打印(p):输出你关心的行
p命令用于打印模式空间中的内容。通常和-n选项一起使用,-n会抑制sed默认的 “自动打印” 行为,只打印我们明确指定的行。
- 打印第 3 行:
bash
sed -n '3p' test.txt
- 打印第 3 到第 5 行:
bash
sed -n '3,5p' test.txt
- 打印所有奇数行(n表示读入下一行):
bash
sed -n 'p;n' test.txt
- 打印包含特定字符串(如PI)的行:
bash
sed -n '/PI/p' test.txt
(2)删除(d):清理无用信息
d命令用于删除模式空间中的行。
- 删除第 3 行:
bash
sed '3d' test.txt
- 删除第 3 到第 5 行:
bash
sed '3,5d' test.txt
- 删除所有空行:
bash
sed '/^$/d' test.txt
- 删除所有以小写字母开头的行:
bash
sed '/^[a-z]/d' test.txt
(3)替换(s):最强大的文本改造
s(substitute)命令是sed中最强大的功能,用于文本替换。它的基本格式是:s/要查找的字符串/替换成的字符串/修饰符。
- 将每行中第一个出现的the替换为THE:
bash
sed 's/the/THE/' test.txt
- 将文件中所有的the替换为THE(使用g修饰符,表示 global):
bash
sed 's/the/THE/g' test.txt
- 在每一行的行首插入一个#号(常用于注释):
bash
sed 's/^/#/' test.txt
- 将文件中所有的字母o删除(替换为空字符串):
bash
sed 's/o//g' test.txt
3. 进阶操作:脚本化与直接编辑
当需要执行多个sed操作时,把它们写在一行会非常混乱。sed允许你通过-f选项,从一个脚本文件中读取并执行命令。
例如,创建一个名为opt.list的脚本文件,内容如下:
plaintext
1,5H
1,5d
17G
然后执行:
bash
sed -f opt.list test.txt
这个脚本会先将 1-5 行的内容复制到剪贴板(H),然后删除这 5 行(d),最后将剪贴板的内容追加到第 17 行之后(G),实现了文本块的移动。
对于需要频繁修改的配置文件,我们甚至可以直接在sed命令中使用-i选项,实现对源文件的 “原地” 编辑,这在自动化脚本中非常方便。
三、awk:字段分析与报告生成器
如果说sed擅长处理 “整行” 的文本,那么awk则更擅长处理 “结构化” 的文本,比如 CSV 文件、系统日志、/etc/passwd这样的配置文件。它把每一行看作一个 “记录”,把行中的每一列看作一个 “字段”,让你可以像操作数据库一样轻松提取和分析数据。
1. awk 的核心概念:记录与字段
- 记录(Record):awk默认将每一行视为一个独立的 “记录”,用内置变量NR(Number of Records)来记录当前处理的是第几行。
- 字段(Field):awk会自动将每一行按照分隔符(默认是空格或制表符)切分成若干个 “字段”。第一个字段是$1,第二个是$2,以此类推,整个行是$0。内置变量NF(Number of Fields)表示当前行有多少个字段。
以 Linux 系统的/etc/passwd文件为例,它的每一行格式是用户名:密码占位符:UID:GID:注释:家目录:登录Shell,字段之间用冒号:分隔。我们可以用-F选项指定分隔符:
bash
awk -F ':' '{print $1, $3, $4}' /etc/passwd
这条命令会打印出所有用户的用户名、UID 和 GID,非常直观。
2. awk 的强大之处:编程能力
awk不仅仅是一个提取工具,它更是一门完整的编程语言,支持变量、数组、条件判断、循环和函数。
(1)内置变量
awk提供了很多方便的内置变量,让你无需重复造轮子:
- FS:字段分隔符(Field Separator),默认是空格。
- NF:当前记录的字段个数。
- NR:当前处理的记录号(行号)。
- $0:当前记录的整行内容。
- FILENAME:当前处理的文件名。
(2)模式与动作
awk命令的基本结构是'模式 { 动作 }'。
- 模式(Pattern):可以是正则表达式、条件表达式,或者BEGIN/END这样的特殊关键字。只有当模式匹配时,后面的动作才会执行。
- 动作(Action):用大括号{}包裹的一系列命令,比如print、变量赋值、循环等。
例如,统计/etc/passwd中以/bin/bash作为登录 Shell 的用户数量:
bash
awk 'BEGIN {x=0}; /\\/bin\\/bash$/ {x++}; END {print x}' /etc/passwd
- BEGIN {x=0}:在处理任何文件之前,先初始化计数器x为 0。
- /\\/bin\\/bash$/ {x++}:对于每一行,如果行尾是/bin/bash,就将计数器x加 1。
- END {print x}:在所有文件处理完毕后,打印出计数器x的值,也就是符合条件的用户总数。
(3)调用外部命令
awk甚至可以通过system()函数或者管道|来调用外部的 Shell 命令,实现更复杂的功能。
例如,在awk中调用wc -l命令来统计用户数:
bash
awk -F: '/bash$/{print | "wc -l"}' /etc/passwd
或者调用hostname命令获取主机名:
bash
awk 'BEGIN {"hostname" | getline; print $0}'
四、总结与实战建议
正则表达式、sed和awk是 Linux 文本处理的三把利刃,它们各有所长:
- 正则表达式是基础,是所有文本匹配的灵魂。
- sed 适合对文本进行快速的行级编辑和转换。
- awk 则在分析和提取结构化数据时表现出色。
在实际工作中,它们经常被组合使用,通过管道|连接起来,形成强大的文本处理流水线。




