精通文本处理:开发者的 Regex、Grep、Sed 与 Awk 指南
Source: Dev.to
请提供您希望翻译的具体文本内容,我将为您翻译成简体中文,并保留原始的格式、Markdown 语法以及技术术语。谢谢!
介绍:Unix 哲学简述
在现代软件开发中,尽管工具链和 IDE 越来越复杂,朴素的命令行仍然是强大与高效的持久堡垒。能够直接在终端中雕刻、搜索和转换文本并不是一种过时的技能——它是一种永恒的能力,能够将熟练的开发者与真正的大师区分开来。这种力量源自 Unix 哲学:一套小而专精的工具,每个工具都旨在做好一件事。当这些工具串联起来时,便能以优雅且清晰的方式完成复杂任务。
本指南提供了一个实用、动手的文本操作基础工具套件之旅。我们将从正则表达式(regex)——描述文本模式的通用语言——开始。随后,我们将探讨三个将该语言付诸实践的基石工具:grep,终极文件搜索器;sed,闪电般快速的流编辑器;以及 awk,用于结构化数据的强大记录处理器。我们的目标不是穷尽所有细节,而是为你提供处理日常 80 % 文本处理挑战的必备知识。
1. 模式语言:正则表达式(Regex)速成课
在我们能够使用这些工具之前,必须先学习它们的语言。正则表达式是一种用于指定文本搜索模式的形式化语法。不要把它们仅仅看作某个特定程序的功能,而应视为一种可移植的、基础的技能,它能在命令行工具、文本编辑器以及 Python、JavaScript 等编程语言中解锁高级功能。掌握正则表达式的核心概念是一项投资,能在整个职业生涯中带来丰厚回报。
1.1. 核心构件
从本质上讲,正则表达式是一串字符,其中一些是 字面量(匹配自身),另一些是 元字符(具有特殊含义)。最基本的元字符用于控制我们如何匹配单个字符、它们的重复次数以及它们在行中的位置。
下面的示例使用 扩展正则表达式(ERE) 以便更清晰。我们将在 1.3 节中介绍 ERE 与较旧的 基本正则表达式(BRE) 语法之间的关键区别,后者是某些工具的默认语法。
1.2. 指定字符集(字符类)
通常,你并不想匹配任意字符,而是想匹配特定集合中的任意字符。方括号表达式 [...] 是定义这些“字符类”的主要机制。
-
匹配特定列表 – 列出你想匹配的确切字符。
示例:[aeiou]匹配任意单个小写元音字母。 -
匹配范围 – 两个字符之间的连字符 (
-) 会创建一个范围,包含它们之间所有字符(依据系统的排序规则)。
示例:[a-z]匹配 ASCII 系统中任意单个小写字母。
注意: 范围的行为高度依赖系统的语言设置(locale)。在 ASCII 中字母是连续的,
[a-z]的行为是可预测的;但在其他语言环境下,字母可能按a, A, b, B, …排序。在这种环境中,[a-c]会意外匹配a, A, b, B, c,而不是预期的a, b, c。这是我们将在 POSIX 字符类章节中解决的关键陷阱。
- 取反集合 – 方括号内部的第一个字符如果是插入符号 (
^),则会反转匹配,使其匹配不在该集合中的任意单个字符。
示例:[^0-9]匹配任何非数字字符。
1.3. 大分水岭:基本正则 vs. 扩展正则(BRE vs. ERE)
对于刚接触命令行的开发者来说,最常见的困惑点之一是存在两套略有不同的正则语法。这是 Unix 演进过程中的历史遗留。
-
基本正则表达式(BRE) – 最初、较旧的语法。
grep、sed等工具默认使用它。在 BRE 中,许多元字符如+、?、|、()会失去特殊含义,必须使用反斜杠 (\) 转义才能激活。 -
扩展正则表达式(ERE) – 更现代、更易读的语法,特殊字符不需要转义。
egrep(或grep -E)和awk等工具使用这种语法。
两者的差异细微却至关重要:
专业提示: 对于任何新脚本,默认使用
grep -E或sed -E的扩展语法。BRE 只是需要了解的历史产物,用于阅读旧脚本;而 ERE 是现代、可读性高的标准。几乎没有充分的理由在新脚本中使用 BRE。
现在我们已经了解了模式语言,让我们看看如何将它与最著名的搭档 grep 一起使用。
2. 使用 grep 在大海捞针
grep(Global Regular Expression Print)是典型的命令行搜索工具。它的目的简单却深远:逐行读取输入,只打印出包含给定模式匹配的行。这使得它在调试代码、分析日志文件以及探索陌生代码库时不可或缺。
2.1. 实际搜索操作
让我们从理论转向实践,看看一些常见的 grep 用例。
示例 1:在日志文件中搜索 IP 地址
要查找特定的 IP 地址,需要对点号进行转义,因为 . 是通配符。使用 \b 表示单词边界可以确保不会匹配到更大数字的子串(例如 101.10.3.20)。
grep '\b1\.10\.3\.20\b' logfile.log
专家提示: 单词边界锚点
\b是 GNU 的强大扩展,但它 不是 POSIX 标准的一部分,可能并非在所有系统上都可用。真正可移植的方式是显式定义边界——通常是空白字符或行首/行尾。例如,匹配完整单词 “book” 的稳健模式可以写成:
grep '(^|[[:space:]])book([[:space:]]|$)' file.txt
2.2. 反向引用的威力
当你在模式中使用圆括号 (...)(在 BRE 中使用 \(...\))时,会创建一个 捕获组。第 n 个组匹配的文本可以在后面使用 \n 引用。
示例
grep '^\(.*\)\1$' /usr/share/dict/words
可能的输出
adad
beriberi
chichi
另一个经典示例:
egrep -v '^(11+)\1+$'
该命令过滤出用一元表示的素数。
3. 可移植性与国际化:POSIX 字符类
类似 [a-z] 的正则在非 ASCII 区域设置下可能失效。POSIX 字符类通过区域感知的集合来解决此问题,例如:
[[:digit:]][[:alpha:]][[:space:]]
3.2. 深入比较:[0-9] 与 [[:digit:]] 与 \d
| 模式 | 作用范围 |
|---|---|
[0-9] | 仅 ASCII |
[[:digit:]] | POSIX 可移植(区域感知) |
\d | 仅限 PCRE / Unicode 环境 |
3.3. 性能秘诀:LC_ALL=C
export LC_ALL=C
grep 'some_pattern' huge_logfile.txt
将 LC_ALL=C 设置为 ASCII 模式,可显著提升处理速度。
4. 实时转换文本使用 sed
sed 是一种 流编辑器。
4.1. 替换语法
s/pattern/replacement/flags
&= 整个匹配\1= 第一个捕获组g= 替换所有匹配
4.2. sed 实际使用
常见用法包括:
- 重排姓名
- 去除 C++ 注释
- 为每行加上引号
5. 使用 awk 对数据进行切片和切块
awk 将输入视为 记录和字段。
5.1. 模型
pattern { action }
关键变量:
$0— 整行$1— 第一个字段NF— 字段数量
5.2. 示例:/etc/passwd
awk -F: '$3 > 1000 { print $1 }' /etc/passwd
6. 最终澄清:正则表达式 与 Shell 通配符
*.txt不是 正则表达式;它是 glob 语法,由 shell 用于文件名展开。
结论:您的命令行工具包
grep— 搜索sed— 转换awk— 处理结构化数据