Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions docs/Ch09/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ icon: material/console

# Shell 高级文本处理与正则表达式

!!! Warning "本文已基本完稿,正在审阅和修订中,不是正式版本。"

!!! abstract "导言"

本章节将介绍一些常用的 shell 文本处理工具,它们可以帮助你更加得心应手地处理大量有规律的文本。
Expand Down Expand Up @@ -122,17 +120,17 @@ $ sort animals | uniq -c

| 特殊字符 | 描述 |
| --------------- | ---------------------------------------------------------------------------------------------------------- |
| `[]` | 方括号表达式,表示匹配的字符集合例如 `[0-9]`、`[abcde]` |
| `[]` | 方括号表达式,表示匹配的字符集合例如 `[0-9]`、`[abcde]` |
| `()` | 标记子表达式起止位置 |
| `*` | 匹配前面的子表达式零或多次 |
| `+` | 匹配前面的子表达式一或多次 |
| `?` | 匹配前面的子表达式零或一次 |
| `\` | 转义字符,除了常用转义外,还有:`\b` 匹配单词边界;`\B` 匹配非单词边界等 |
| `\` | 转义字符 |
| `.` | 匹配除 `\n`(换行)外的任意单个字符 |
| `{}` | 标记限定符表达式的起止。例如 `{n}` 表示匹配前一子表达式 n 次;`{n,}` 匹配至少 n 次;`{n,m}` 匹配 n 至 m 次 |
| <code>\|</code> | 表明前后两项二选一 |
| `$` | 匹配字符串的结尾 |
| `^` | 匹配字符串的开头在方括号表达式中表示不接受该方括号表达式中的字符集合 |
| `^` | 匹配字符串的开头在方括号表达式中表示不接受该方括号表达式中的字符集合 |

以上特殊字符,若是想要匹配特殊字符本身,需要在之前加上转义字符 `\`。

Expand Down Expand Up @@ -162,14 +160,16 @@ $ sort animals | uniq -c
ter\b
```

### 基本/扩展正则表达式 {#bre-ere}
### 基本、扩展与 Perl 兼容正则表达式 {#bre-ere-pcre}

基本正则表达式(Basic Regular Expressions, BRE)和扩展正则表达式(Extended Regular Expressions, ERE)是两种 POSIX 正则表达式风格。

BRE 可能是如今最老的正则风格了,对于部分特殊字符(如 `+`, `?`, `|`, `{`)需要加上转义符 `\` 才能表达其特殊含义。

ERE 与如今的现代正则风格较为一致,相比 BRE,上述特殊字符默认发挥特殊作用,加上 `\` 之后表达普通含义。

除 POSIX BRE/ERE 外,实际使用中还经常遇到 [PCRE(Perl Compatible Regular Expressions)](https://www.pcre.org/)。PCRE 在 ERE 类似语法的基础上提供了更多现代正则表达式功能,例如 `\d`、`\w`、非捕获分组 `(?:...)`、以及惰性匹配 `*?` 等(具体内容可阅读[拓展阅读](./supplement.md))。

具体的例子在下文介绍工具时可以看到。

!!! tip "帮助理解正则表达式的工具"
Expand All @@ -182,25 +182,27 @@ ERE 与如今的现代正则风格较为一致,相比 BRE,上述特殊字符

grep 全称 Global Regular Expression Print,是一个强大的文本搜索工具,可以在一个或多个文件中搜索指定 pattern 并显示相关行。

grep 默认使用 BRE,要使用 ERE 可以使用 `grep -E` 或 egrep。
grep 默认使用 BRE,要使用 ERE 可以使用 `grep -E` 或 egrep,要使用 PCRE 可以使用 `grep -P`

命令格式:`grep [option] pattern file`

一些用法
常见参数

- `-n`:显示匹配到内容的行号
- `-v`:显示不被匹配到的行
- `-i`:忽略字符大小写
- `-R`:递归搜索目录下的所有文件

```console
$ ls /bin | grep -n "^man$" # 搜索内容仅含 man 的行,并且显示行号
$ ls /bin | grep -v "[a-z]\|[0-9]" # 搜索不含小写字母和数字的行
$ ls /bin | grep -iv "[A-Z]\|[0-9]" # 搜索不含字母和数字的行
$ ls /bin | grep -R "man" # 递归搜索 /bin 下所有文件中含有 man 的行
```

### sed {#sed}

sed 全称 Stream EDitor,即流编辑器,可以方便地对文件的内容进行逐行处理。
sed 全称 Stream EDitor,即流编辑器,可以方便地对文件的内容进行逐行处理。sed 每次从文件中读取一行,然后使用指定的命令进行操作,操作完成后输出结果。

sed 默认使用 BRE,要使用 ERE 可以 sed -E。

Expand All @@ -213,9 +215,15 @@ $ sed [OPTIONS] -f scriptfile file(s)

此处的 command 和 scriptfile 中的命令均指的是 sed 命令。

常见参数:

- `-i`:直接修改文件内容,可以加上后缀 `-i[SUFFIX]` 备份原文件
- `-n`:不自动输出每一行,通常和 p 命令配合使用,只输出被匹配到的行

常见 sed 命令:

- s 替换
- 格式为 `s/old/new/flag`,其中 `old` 和 `new` 分别为要替换的内容和替换后的内容(可以使用正则表达式),`flag` 可选,常用的有 `g`(全局替换)、`p`(打印匹配行)、`i`(忽略大小写),`/` 表示分隔符,常见的分隔符包括 `/`、`#`、`@`、`|`、`,`、`;`、`:`、`_`、`^`、`~`、`!` 等,若要使用分隔符本身,则需要加上转义符 `\`。
- d 删除
- c 选定行改成新文本
- a 当前行下插入文本
Expand Down Expand Up @@ -313,11 +321,11 @@ $ awk 'BEGIN { sum = 0 } { sum += $2 * $3 } END { print sum }' awk_demo

可以接受少许的误过滤(false positive)。

!!! question "正则表达式练习 3:Vscode 中的文本批量替换"
!!! question "正则表达式练习 3:VS Code 中的文本批量替换"

Vscode 支持使用正则表达式语法查找与替换文本内容。有一天,你的项目中使用的某个函数更新了:调用方式从 `func1(a, b, c)` 变成了 `func1_new(c, b, a, null)`。其中假设 `a`、`b`、`c` 均为不含逗号的表达式。
VS Code 支持使用正则表达式语法查找与替换文本内容。有一天,你的项目中使用的某个函数更新了:调用方式从 `func1(a, b, c)` 变成了 `func1_new(c, b, a, null)`。其中假设 `a`、`b`、`c` 均为不含逗号的表达式。

尝试写出搜索的正则表达式与替换目标表达式。提示:正则表达式中使用 `()` 包裹的为一组,在 vscode 的替换目标表达式中可以使用 `$1`、`$2` 的格式来引用第一组、第二组等内容。
尝试写出搜索的正则表达式与替换目标表达式。提示:正则表达式中使用 `()` 包裹的为一组,在 VS Code 的替换目标表达式中可以使用 `$1`、`$2` 的格式来引用第一组、第二组等内容。

!!! question "Shell 文本处理工具练习 1:文件内容替换"

Expand Down
5 changes: 2 additions & 3 deletions docs/Ch09/supplement.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ icon: material/puzzle
组号分配规则:

- 0 代表整个表达式
- 从左至右,按左括号的出现顺序分配,第一个为 1,第二个为 2,以此类推
- 扫描两遍,第一次只分配未命名的组,第二次只分配命名的组。即任意命名组的组号都大于未命名的组号
- 从左至右,按左括号的出现顺序分配,第一个为 1,第二个为 2,以此类推,命名组与未命名组共享同一编号序列,即命名组同样会占用一个数字组号

!!! example "示例"

Expand All @@ -42,7 +41,7 @@ icon: material/puzzle

`\b\w+(?=ing\b)` 可以匹配 `ing` 结尾的单词前面的部分。例如 `going` 中的 `go` 会被该正则匹配。

`(?<=\bre)\w+\b` 可以匹配 `re` 开开头的单词的后面一部分。例如 `revue` 中的 `vue` 会被该正则匹配。
`(?<=\bre)\w+\b` 可以匹配 `re` 开头的单词的后面一部分。例如 `revue` 中的 `vue` 会被该正则匹配。

### 正则表达式的代价 {#ddos}

Expand Down