正则和文本处理

正则表达式

什么是正则表达式

正则表达式是一种符号表示法,用于识别文本模式,在某种程度上,它们类似于匹配文件和路径名时使用的 shell 通配符,但其用途更加广泛

grep 文本搜索

我们用来处理正则表达式的主要程序是 grep ,它的名字起源于 global regular expression print ,实际上, grep 搜索文本文件中与指定正则表达式匹配的行,将结果送至标准输出

grep 按照下列方式接受选项和参数

grep [options] regex [file...]

下面列出了 grep 常用的选项

  • -i:忽略大小写。可以用--ignore-case代替
  • -v:不匹配。正常情况下,grep会输出匹配行,而该选项可使grep输出不包含匹配项的所有行,可以用--invert-match指定
  • -c:输出匹配项数目,如果有-v输出不匹配行,可以用--count指定
  • -l:输出匹配文件名而不是直接输出匹配行自身。可以用--files-with-matchs指定
  • -L:与-l类似,但输出的是不包含匹配项的文件名,可以用--files-without-match指定
  • -n:在每个匹配行加上该行在文件内的行号,可以用--line-number指定
  • -h:进行多文件搜索时,抑制文件名输出。也可以用--no-filename指定

元字符和文件

grep 搜索一直在使用正则表达式,除了文字字符,正则表达式还可以包含用于指定更为复杂的匹配的元字符,它包含以下字符

^ $ . [ ] { } - ? * + ( ) | \

在极少数情况下,反斜杠字符用来创建元字符,以及用来对元字符进行转义,使其称为文字字符。在命令行中输入包含元字符的正则表达时,应该把这些元字符用引号括起来以避免不必要的 shell 扩展

. 匹配任意字符

^$ 开头和结尾

[] 字符集,其中包含 ^ 表示否,包含 - 表示字符范围,由于 UNIX 希望匹配更多的字符集,所以推出了 POSIX 标准,它包含了许多标准字符类,这些字符类提供了一些有用的字符范围

  • [:alnum:]字母和数字字符,在 ASCII 码中,和[A-Za-z0-9]等效
  • [:word:]基本与[:alnum:]等效,只是多了_
  • [:alpha:]字母字符;在 ASCII 中,等效于[A-Za-z]
  • [:blank:]包含空格和制表符
  • [:cntrl:]ASCII 控制码;包括 ASCII 字符0~31以及127
  • [:digit:]数字0~9
  • [:graph;]可见字符,在 ASCII 中,包含字符33~126
  • [:lower:]小写字母
  • [:punct:]标点符号字符;在 ASCII 中,与[-!"#$%&'()*+,/:;<=>?@[\\\\\\]_{|}~]等效
  • [:print:]可打印字符,包括[:graph]中所有字符再加上空格字符
  • [:space:]空白字符如空格符、制表符、回车符、换行符、垂直制表符以及换页符,在 ASCII 中,等效为[\t\r\n\v\f]
  • [:upper:]大写字母
  • [:xdigit:]用于表示十六进制的字符;在 ASCII 中,与[0-9A-Fa-f]等效

| 或选项,用于从字符串集或正则表达式中寻找匹配项

限定符 ? 匹配 0 次或 1 次

* 多次或零次

+ 一次或多次

{} 指定次数匹配

文本处理

由于所有类 UNIX 操作系统都依赖于文本文件来进行某些数据类型的存储,所以需要有很多可以进行文本操作的工具,本章主要介绍一些和“切割”有关的命令

文本应用程序

文件

我们可以使用文本格式编辑较大的文档,有一种很常见的方法,即首先在文本编辑器中编辑大型文档的内容、然后使用标记语言描述文件格式

网页

网页属于文本文档,一般使用 HTML 或 XML 等标记语言描述内容的可视化形式

电子邮件

电子邮件本质上是一种基于文本的媒介,即便是非文本附件,在传输的时候也会转成文本格式。

打印机输出

准备向打印机传送的信息是以纯文本格式传送的,如果该页包含图像,则将其转换为 PostScript 文本格式页面描述语言后再送至指定程序

程序源代码

它们中多数都是为解决软件开发问题而设计的。文本处理对软件开发者如此重要,是因为所有软件都是从文本开始的

温故以求新

cat-进行文本之间的拼接且输出到标准输出

cat 命令有很多有趣的参数选项,其中的多数则是用于提高文本内容的可视化效果, -A 选项就是一个例子,它用于显示文本中的非打印字符。其中最为常见的就是制表符以及回车符。另一种常见情况是包含末尾带有空格的文本行

我们创建一个测试文件,用 cat 程序作为一个简单的文字处理器,为此,只需要输入 cat 命令再输入文本内容,按 Enter 键结束行输入,最后按下 Ctrl-D 告诉 cat 到达文件末尾。

cat > foo.txt

cat 也有很多用于修改文本的参数选项,比如 -n ,对行编号; -s ,禁止输出多个空白行

sort 对文本行进行排序

sort 是一个排序程序,它的操作对象为标准输入或是命令行中指定的一个或多个文件后将结果送至标准输出。与 cat 用法类似,由于 sort 命令允许多个文件作为其输入参数,所以可以将多个文件融合成一个已排序的整体文件。例如,我们有三个文本文件,并期望将它们拼接成一个已排序的整体文件

sort file1.txt file2.txt file3.txt file4.txt > final_sorted_list.txt

sort 有一些有趣的选项

-b--ignore-leading-blanks 默认情况下,整个行都会进行排序操作,也就是从行的第一个字符开始。添加该选项后, sort 会忽略行开头的空格,并且从第一个非空字符开始排序

-f--ignore-case 排序时不区分字符大小写

-n--numeric-sort 基于字符串长度进行排序。该选项使得文件按设置顺序而不是字母表顺序进行排序

-r--reverse 逆序排序,输出结果按照降序排序而不是圣墟

-k--k=field1[,field2]field1field2 之间的字符进行排序

-m--merge 将每个输入参数当做已排好的文件名,将多个文件合并为一个排好序的文件,而执行额外的排序操作

-o--output=file 将排序结果输出到文件而不是标准输出

-t--field-separator=char 定义字段分隔符。默认情况下字段是由空格或制表符分开的

uniq-通知或省略重要的行

sort 相比, uniq 算是一个轻量级命令。 uniq 执行的是一个看似简单的任务,给定一个已排好序的文件后, uniq 会删除任何重复的行并将结果输出到标准输出中。它通常与 sort 结合使用以删除 sort 输出内容中重复的行

切片和切块

下面讨论的三个命令,它们的作用是玻璃文本文件的列,并将它们以期望的方式重组

cut-删除文本行中的部分内容

cut 命令用于从文本行中提取一段文字并将其输出至标准输出。它可接受多个文件和标准输入为其输入参数

下列是指定时需要用到的选项

-cchar_list 从文本行中提取 char_list 定义的部分内容,此列表可能会包含一个或更多冒号分开的数值范围

-ffield_list 从文本中提取 field_list 定义的一个或多个字段,该列表可能会包含由冒号分隔的一个、多个字段或字段范围

-ddelim_char 指定 f 选项后,使用 delim_char 作为字段分界符。默认时,字段必须以单个 Tab 制表符隔开

--complement 从文本中提取整行,除了那些由 -c/-f 指定的部分

cut 适合从其他命令的输出结果中提取文本内容,而不是直接从输入文本中提取

paste 合并文本行

paste 命令是 cut 的逆操作,他不是从文本文件中提取列信息,而是项文件中增加一个或更多的文本列,该命令读取多个文件并将每个文件中提取出的端结合为一个整体的标准输出列

join 连接两个文件中具有相同字段的行

从某种程度上来说, joinpaste 类似,因为它也是增加列信息,只是实现方式有些不同。 join 操作通常和“关联数据库”连接在一起,它在关联数据库中把共享关键字段多个表格的数据组合成一个期望结果。

文本比较

比较文本文件的版本号通常很有用,例如一个系统单管理者可能需要将已存在的配置文件与原先的配置文件相比较,以检查出系统漏洞,

comm 逐行比较两个已排序文件

comm 命令一般用于文本文件之间的比较,显示两个文件中相异的行以及相同的行

comm file1.txt file2.txt

结果的第一列显示第一个文件中独有的行,第二列显示第二个文件中独有的行,第三列是共有的行

diff 逐行比较文件

comm 类似, diff 用于检测文件之间的不同,但是 diffcomm ,它支持多种输出形式,并且具备一次性处理大文件集的能力。 diff 通常被软件开发者用哪个来检查不同版本的源代码之间的差异,因为它能够递归检查源代码目录。 diff 的常见用法就是创建 diff 文件和补丁,它们可以为诸如 patch 这样的命令所用,宠而实现一个版本的文件更新为另一个版本

diff 默认文件的输出结果是对两者文件差异的一个简洁描述

diff file1.txt file2.txt 输出为

// 默认形式中,每一组改动的前面都有一个以“范围 执行操作 范围“形式表示的改变操作命令
// 该命令会告诉程序对第一个文件的某个位置进行某种改变,便可实现与第二个文件内容一致
// r1cr2 用第二文件 r2 处的行代替第一个文件 r1 处的行
// r1dr2 删除第一个文件 r1 处的行,并且删除的内容作为第二个文件 r2 行范围的内容
1d0
< a
// r1ar2 将第二个文件中 r2位置的行添加到第一个文件中的 r1处
4a4
> e

上下文格式和统一格式才是比较普遍的格式 diff -c file1.txt file2.txt

// 上下文格式
**** 1,4 ****  // 表示第一个文件的第 1 行至第 4 行
- a  // - 表示缺少的行
  b
  c
  d // 表示第二个文件的第 1 行至第 4 行
	b
		 // (空)

  c
	d
+ e  // 多余的行
// ! 表示改变的行,两个版本行的内容都会显示出来,每一个都各自出现在差异组中相应的部分

// 统一格式
@@ -1,4 +1,4 @@
- a
  b
  c
  d
+ e

patch 对原文件进行 diff 操作

patch 命令用于更新文本文件。它利用 diff 命令的输出结果将较旧版本的文件升级成较新版本。比如对 linux 源代码的改动只要发送 diff 补丁即可, patch 命令将这些改变应用于吱声的源代码书。 diff/patch 有两个重要的优点

  • 与源代码树的大小相比,diff文件很小
  • diff文件非常简洁地描述文件所做的改变,便于补丁接受者快速对其进行评价

diff/patch 不仅仅局限于源代码,它适用于任何文本文件,因此,它同样适用于配置文件以及其他文本文件

生成供 patch 使用的 diff 文件,GNU 文件系统建议采用如下方式使用 diff

diff -Naur old_file new_file > diff_file
pach < diff_file

非交互式文本编辑

我们也可以只用一个简单的命令一次性更改多个文件

tr 替换或删除文件

tr 是替换字符命令,可以将其看作一种基于字符的查找和替换操作。所谓替换,实际是将字符从一个字母更换为其他字母,例如,将小写字母转换为大写字母。

echo "lowercase letters" | tr a-z A-Z` => `LOWERCASE LETTERS

tr 有两个参数:等待转换的字符集和与之对应的替换字符集,字腹肌的表示方法可以是下面三种方式中的任意一种

  • 枚举列表:比如ABCDEFG
  • 字符范围:例如 A-Z
  • POSIX 字符类:例如[:upper:]

多数情况下,这两个字符集应该是同等长度,然而,第一个字符集比第二个字符集长也是可能的。

echo "Lowercase letters" | tr [:lower:] A` => `AAAAAAAA AAAAAAA

除了替换, tr 还可以直接从输入流中删除字符,比如讲 MS-DOS 类型的文件向 UNIX 类型转换的问题,通过移除每行末尾的回车符

tr -d '\r' < dos_file > unix_file

tr 还可以删除重复字符,但是注意重复字符必须相邻

echo "aaabbbccc" | tr -s ab` => `abccc

sed 用于文本过滤和转换的刘编辑器

sedstream editor(流式编辑器) 的缩写,它可以对文本流、指定文件集或标准输入进行文本编辑。这个命令非常复杂,有整本书对其进行了讲解,本书无法涉及所有用法

echo "front" | 's/front/back/' 该例先通过 echo 生成了包含一个单词的文本流,然后将该文本流交给 sed 处理,而 sed 则对文本流执行 s/front/back/ 指令,最后输出 back 作为运行结果,它和替代命令相似

sed 命令总是以单个字母开头,其后紧跟替换字符,替换字符由作为分界线的斜线字符分开,分界符的选择是随意的,习惯上一般使用斜线

sed 多数命令允许在其前面添加一个地址,该地址用来指定输入流的哪一行被编辑,如果该地址省略,则默认对每一行进行编辑,比如

echo "front" | sed '1s/front/back/'` => `back
  • Nn是正整数表示行号
  • $:最后一行
  • /regexp/:用POSIX基本正则表达式描述的行。注意这里的正则表达式使用斜线作为分隔符,也可以自己使用分隔符
  • addr1, addr2:行范围,表示从addr1addr2之间的所有行
  • first~step:代表行号从first行开始,以step为间隔的所有行,比如,1~2表示所有奇数行,5~5指第 5 行和随后所有是5的倍数的行
  • addr1, +naddr1及其之后的n
  • addr!:除了addr行之外的所有行

s 命令的替换字符串后面可以紧跟可选择标准符

echo "aaabbbccc" | sed 's/b/B/'` => `aaaBbbccc`,此时默认替换匹配的第一个字符,我们可以加上`g
echo "aaabbbccc" | sed 's/b/B/g'` => `aaaBBBccc

可以使用 p 命令输出指定匹配行的内容

sed -n '1,5p' distros.txt

sed 命令一般用于简单的但命令行任务而不是长脚本,许多用户偏向于其他工具,比如 awkperlperl 通常取代 shell 脚本用于执行系统管理和管理员任务,而 awk 则是用于数据处理,它也是逐渐处理文本文件

aspell 交互式拼写检查工具

aspell 命令继承的是早期的 ispell 命令,在多数情况下,可以直接取代 ispell 命令。虽然 aspell 命令通常为那些需要进行拼写检查的程序所用,但它同样可以作为一个独立于命令行的工具发挥其效用。他可以智能地检查不同类型文本文件的错误,包括 HTML 文件、C/C++ 程序、email 消息以及其他专业的文本文件

检查一篇简单散文的拼写错误,可以像如下使用 aspell

aspell check testfile 这里的 testfile 就是要进行检查的文件名

比如我们要检查 HTML 文件的拼写错误

aspell -H check foo.txt 使用 -H 结果会忽略 HTML 标签进行交互