使用正则表达式处理文本

LinuxBeginner
立即练习

简介

正则表达式通过文本结构描述文本,而不是匹配某个完全固定的字符串。许多 Linux 工具都使用相同的模式语言,但使用方式各不相同:grep 用于筛选或提取匹配项,sed 用于转换文本流,awk 则将模式与基于字段的操作结合起来。

你将先学习锚点和字符类,然后使用扩展重复和提取功能,预览转换结果后再保存,最后使用字段筛选和汇总。为模式加上引号,可以让 shell 将特殊字符传递给文本工具,而不是由 shell 自身解释。

使用基本正则结构匹配行

在这一步中,你将使用字面文本、锚点和字符类来描述整行中的位置。

查看练习文件:

cd /home/labex/project/regex-lab
nl -ba contacts.txt

字面文本可以匹配行中的任意位置:

grep 'active' contacts.txt

^ 将模式锚定在行首,$ 将模式锚定在行尾。查找 Alice 的记录,以及以两位数字结尾的记录:

grep '^Alice|' contacts.txt
grep '[0-9][0-9]$' contacts.txt

在方括号中,字符类匹配一个字符。[ABC] 表示 A、B 或 C:

grep '^[ABC]' contacts.txt | tee abc-contacts.txt

该命令会输出 Alice、Bob 和 Carol 的记录。接着保存以数字结尾的匹配结果:

grep '[0-9][0-9]$' contacts.txt > two-digit-values.txt

使用扩展正则表达式并提取匹配项

在这一步中,你将使用扩展正则表达式表示多个候选项和重复模式,然后只提取匹配的子字符串。

grep -E 启用 +(一个或多个)、?(零个或一个)和 |(候选项)等运算符。匹配完整的 active 或 pending 状态字段:

cd /home/labex/project/regex-lab
grep -E '\|(active|pending)\|' contacts.txt

可以分段构造类似电子邮件地址的模式:

  • [[:alnum:]._-]+ 匹配一个或多个允许出现在本地部分中的字符;
  • @ 是字面字符;
  • [[:alnum:].-]+ 匹配域名主体;
  • \.[[:alpha:]]{2,} 匹配一个点号以及由两个或更多字母组成的后缀。

使用 -o 只输出匹配的子字符串:

grep -Eo '[[:alnum:]._-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}' contacts.txt | tee emails.txt

为匹配的行编号,并统计提取出的地址数量:

grep -En '\|(active|pending)\|' contacts.txt
wc -l < emails.txt | tr -d ' ' > email-count.txt
cat email-count.txt

格式错误的 dave_at_example.com 不会匹配,因为其中没有 @

使用 Sed 预览转换结果

在这一步中,你将使用 sed 转换文本流,同时保持输入文件不变。

替换命令的形式是 s/pattern/replacement/flags。先将每行中的第一个 a 替换为大写的 A,然后使用 g 标志替换每行中的所有小写 a

cd /home/labex/project/regex-lab
sed 's/a/A/' contacts.txt
sed 's/a/A/g' contacts.txt

如果文本本身包含斜杠,或者使用其他分隔符更清晰,可以改用不同的分隔符。将电子邮件域名替换为 redacted.invalid

sed -E 's#@[[:alnum:].-]+\.[[:alpha:]]{2,}#@redacted.invalid#g' contacts.txt

Sed 地址用于选择要执行命令的行。只对包含精确 |inactive| 字段的行执行状态替换:

sed '/|inactive|/s/|inactive|/|disabled|/' contacts.txt | tee contacts-preview.txt

确认原文件中仍然包含 inactive

grep '|inactive|' contacts.txt

使用 Sed 创建清理后的文件

在这一步中,你将组合多个 sed 命令,并将转换后的文本流保存为新文件。

services.csv 中的表头应保持不变。对于数据行,将 warn 统一替换为 warning,并为最后一个数字字段添加 ms 后缀。-e 用于提供多个命令,地址 2,$ 将命令限制在第 2 行到最后一行:

cd /home/labex/project/regex-lab
sed -E -e '2,$ s/,warn,/,warning,/' -e '2,$ s/([0-9]+)$/\1ms/' services.csv | tee services-clean.csv

这里,圆括号会捕获末尾的数字,\1 会在替换文本中重新使用该捕获内容。

比较原文件和新文件:

diff -u services.csv services-clean.csv || true

由于输出是有意修改过的,diff 返回非零状态是预期结果;|| true 可以让复制的命令序列继续执行。

使用 Awk 选择字段并汇总

在这一步中,当任务同时需要字段结构和计算时,你将使用 awk

-F, 将逗号设置为输入分隔符。仅为数据行打印服务名称和状态;NR 表示当前记录编号:

cd /home/labex/project/regex-lab
awk -F, 'NR > 1 {print $1, $2}' services.csv

筛选第三个数字字段大于 200 的行:

awk -F, 'NR > 1 && $3 > 200 {print $1, $3}' services.csv | tee slow-services.txt

累加总数并计数,然后在 END 代码块中计算平均值。printf 用于控制数字格式:

awk -F, 'NR > 1 {sum += $3; count++} END {printf "average_latency=%.1f\n", sum/count}' services.csv | tee latency-summary.txt

Awk 模式决定哪些记录会执行操作;$1$3 等字段可以让结构化计算更加清晰。

总结

你已经使用锚点、字符类、候选项和重复构建了正则表达式模式;使用 grep 进行筛选和提取;使用 sed 预览并保存转换结果;还使用 awk 完成了基于字段的筛选和汇总。接下来的挑战要求你应用这些方法,而不会提供完整的命令序列。