第二十章 文本处理
第二十章 文本处理
所有类UNIX系统都严重依赖于文本文件来存储数据,所以存在大量文本操作工具也在情理之中。
相关命令:
- cat:拼接文件。
- sort:排序文本行。
- uniq:报告或忽略重复的行。
- cut:从每行中删除部分内容。
- paste:合并行。
- join:连接两个文件中具有公共字段的行。
- comm:逐行比较两个已排序的文件。
- diff:逐行比较文件。
- patch:对原文件应用diff文件。
- tr:转写或删除文件。
- sed:用于文本过滤和转换的流编辑器。
- aspell:交互式拼写检查器。
文本的应用
到目前为止,学习过两种文本编辑器(Nano和Vim),看过多个配置文件,也看到过许多命令的输出结果,它们都离不开文本。除此之外,文本还应用到很多地方。
文档
很多人都使用纯文本格式编写文档。虽然不难看出用小文本文件记录些简单的笔记确实有用,但编写文本格式的大型文档也是可行的。有一种流行的方式就是先采用文本格式编写大型文档,然后在其中嵌入标记语言来描述最终文档的格式。许多科技论文就是这样写出来的。
网页
世界上流行的电子文档类型包括网页了。网页是使用超文本标记语言(Hypertext Markup Language, HTML)或可扩展标记语言(Extensible Markup Language, XML)来描述文档可视格式的文本文档
电子邮件
电子邮件本质上是一种基于文本的媒介。就算是非文本附件,在传输的时候也会被转换为文本格式。邮件消息以标题(header)开头,描述了该邮件的来源以及其传输过程中所进行的处理,然后是邮件消息正文(body)。
打印机输出
在类UNIX系统中,输出到打印机的消息是以纯文本格式发送的,如果待打印页面包含图像,则将其转换为称作PostScript的文本格式页面描述语言的描述信息,然后发送到负责生成待打印图形点(graphic dots)程序
程序源代码
类UNIX系统中有不少命令行程序都是为了支持系统管理和软件开发而编写的,文本处理程序也不例外,其中有很多是为解决软件开发问题而设计的。文本处理对软件开发者重要的原因在于所有的软件一开始都是文本。软件开发者实际编写的程序源代码始终都是文本格式。
温故知新
在第6章中,提到了一些除命令行参数之外还能接受标准输入的命令。不过当时只是"一笔带过",现在详细了解这些命令如何进行文本处理。
cat——连接文件并打印
cat命令中,有不少选项可用于增强文本内容的视觉效果。-A就是一个例子,它能够显示文本中的非输出字符。有时想知道可见文本中是否嵌有控制字符。常见的控制字符就是制表符和回车符,后者多见于MS-DOS风格的文本文件的行尾。另一种常见情况是文本文件中包好末尾带有空格的文本行。
将cat命令作为一个简单的文本处理器。输入了一个以制表符开头,若干空格结尾的文本行。
以默认方式输出文本:
cat > foo.txt
输出内容如下:
The quick brown fox jumped over the lazy dog.
使用带有-A选项的cat显示文本:
cat -A foo.txt
输出内容如下:
^IThe quick brown fox jumped over the lazy dog. $
可以看到,文本中的制表符由^I表示。这是一种常见表示法,代表Ctrl-I。还可以看到$出现在真正的行尾,这说明文本中包含结尾空格。
有些cat选项可用于修改文本,其中较突出的两个分别是-n或-s,前者能够为文本行加上行号,后者能够禁止输出连续的空白行。
例如:
[me@linuxbox ~]$ cat > foo.txt
The quick brown fox
jumped over the lazy dog.
[me@linuxbox ~]$ cat -ns foo.txt
1 The quick brown fox
2
3 Jumped over the lazy dog.
在这个例子中,我们创建了一个新版本的foo.txt测试文件,其中包含被两个空白分隔的两行文本。经cat的-ns选项处理之后,多余的空白行被删除了,剩下的文本行也被加上了行号。虽然这算不上复杂的处理过程,但文本的确经过了处理。
sort-排序
sort对标准输入内容或命令行上指定的文件进行排序,并将结果发送至标准输出。
例如:对文件foo.txt进行排序:
foo.txt的内容:
c
a
b
sort foo.txt
输出结果如下:
a
b
c
从输出结果可以看出,sort是按字典序对字母进行排序的。
sort可以接受命令行上指定的多个文件作为参数,所以能将多个文件合并成单个文件。
例如:假设有3个文本文件,我们想将其合并成一个有序文件,可以这样做:
sort file1.txt file2.txt file3.txt > final_sorted_list.txt
常用的sort选项
选项 | 描述 |
---|---|
-b, --ignore-leading-blanks | 在默认情况下,对整行进行排序,从行内的第一个字符开始。该选项使sort忽略每行开头的空白字符,从第一个非空白字符开始排序 |
-f, --ignore-case | 排序时不区分大小写 |
-n, --numeric-sort | 根据字符串的数值(numberic evaluation)排序。该选项使排序按照数值顺序,而不按照字母表顺序进行 |
-r, --reverse | 降序排序。输出结果按照降序排列 |
-k, --key=field1[,field2] | 不再按照整行,而是按照由区间范围[field1,field2]指定的关键字字段进行排序。 |
-m,–merge | 每个参数被视为预排序过的文件名称。将多个文件合并单成单个有序结果,不再执行额外的排序 |
-o, --output=file | 将排序后的输出发送至file,而非标准输出 |
-t, --field-separator=char | 定义字段分隔符。在默认情况下,字段由空格符或制表符分隔 |
例如:
对数值数据进行排序,需要通过du命令获取各个目录的占用磁盘空间,du命令格式如下:
du -s path #统计path下各个字母录所占用的磁盘空间。
对/usr/share下的子目录的占用磁盘空间进行降序排序:
du -s /usr/share/* | sort -nr | head
命令结果如下:
509940 /usr/share/local-langpack
242660 /usr/share/doc
197560 /usr/share/fonts
179144 /usr/share/gnome
146764 /usr/share/myspell
144304 /usr/share/gimp
135880 /usr/share/dict
76508 /usr/share/icons
68072 /usr/share/apps
62844 /usr/share/foomatic
通过-n和-r选项,按照数值进行降序排序,使最大值出现了结果最前面。之所以可行的原因在于数值位于每行的开头位置。如果像根据行内的某个值来排序。下面是ls -l的输出结果:
[me@linuxbox ~]$ ls -l /usr/bin | head
total 152948
-rwxr-xr-x 1 root root 34829 2016-04-04 02:42 [
-rwxr-xr-x 1 root root 101556 2007-11-27 06:08 a2p
-rwxr-xr-x 1 root root 13036 2016-02-27 08:22 aconnect
-rwxr-xr-x 1 root root 10552 2007-08-15 10:34 acpi
-rwxr-xr-x 1 root root 3800 2016-04-14 03:51 acpi_fakekey
-rwxr-xr-x 1 root root 7536 2016-04-19 00:19 acpi_listen
-rwxr-xr-x 1 root root 3576 2016-04-29 07:57 addpart
-rwx-xr-x 1 root root 20808 2016-01-03 18:02 addr2line
-rwx-xr-x 1 root root 489704 2016-10-09 17:02 adept_batch
使用sort进行文件大小排序:
[me@linuxbox ~]$ ls -l /usr/bin | sort -nrk 5 | head -n 5
-rwxr-xr-x 1 root root 8234216 2016-04-07 17:42 inkscape
-rwxr-xr-x 1 root root 8222692 2016-04-07 17:42 inkview
-rwxr-xr-x 1 root root 3746508 2016-03-07 23:45 quanta
-rwxr-xr-x 1 root root 3654020 2016-08-26 23:45 gimp-2.4
-rwxr-xr-x 1 root root 2928760 2016-09-10 14:31 gdbtui
sort的许多用法都涉及表格型数据(tabular data)处理,例如上述ls命令的输出结果。如果套用数据库的术语,可以称每行是一条记录,每列是一个字段,诸如文件属性、链接数、文件名、文件大小等。sort能够单独的字段,用数据库的术语来说,可以指定一个或多个关键字段(key fields)作为排序关键字(sort keys)。指定了-n和-r选项,按照数值进行降序排序,同时还制定了-k 5,使sort用第5个字段作为排序关键字。
-k选项具备不少特征,这些特征值得注意,先介绍sort定义字段的。考虑一个简单的文本文件,其中只有一行内容,包含作者和姓名:
William Shotts
在默认情况下,sort将该行视为两个字段。第一个字段包含字符串Willian,第二个字段包含字符串Shotts。
这意味着空白字符(空格符和制表符)被用作字段之间的分隔符,在进行排序时,分隔符包括在字段中。
ls输出包含8个字段,其中第5个字段是文件大小:
-rwxr-xr-x 1 root root 8234216 2016-04-07 17:42 inkscape
sort允许出现多个-k选项,这样救就能指定多个排序关键字。关键字可以是字段范围。如果没有指定范围,则从指定字段开头,一直延续到行尾。
采用短选项-k 1,1 -k 2n,将排序限制在第一个字段中,所以指定1,1,意思时"从字段1开始,至字段1结束“。第二个-k选项指定了2n,意思是字段2作为排序关键字且按照数值进行排序。这些选项字母和sort的全局选项一样:b(忽略开头的空白字符)、n(数值排序)、r(降序排序)等。
列表中第3个字段包含的日期格式不适合排序。在计算机中,日期通常采用年-月-日的形式,以便按时间顺序进行排序,但这里采用的是月/日/年格式。sort提供了-k选项允许指定字段偏移,这样就可以指定关键字了:
sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt
该命令输出结果如下:
Fedora 10 11/25/2008
Ubuntu 8.10 10/30/2008
SUSE 11.0 06/19/2008
Fedora 9 05/13/2008
Ubuntu 8.04 04/24/2008
Fedora 8 11/08/2007
Ubuntu 7.10 10/18/2007
通过指定-k 3.7, 告知sort排序关键字从第3个字段的第7个字符开始(对应于年份)。与此类似,分别指定-k 3.1和 -k 3.4,划分出日期中的月和日,另外还加入了-n和-r选项来实现数值降序排序。-b选项用于禁止日期字段开头的空格。
sort提供了 -t选项来定义字符分隔符。
例如:按照第七个字段排序passwd文件,可以这样做:
sort -t ':' -k 7 /etc/passwd | head
命令输出如下:
me❌1001:1001:Myself,:/home/me:/bin/bash
root❌0:0:root:/root:/bin/bash
dhcp❌101:102::/nonexistent:/bin/false
gdm❌106:114:Gnome Display Manager:/var/lib/gdm:/bin/false
hplip❌104:7:HPLIP system user,:/var/run/hplip:/bin/false
klog❌103:104::/home/klog:/bin/false
messagebus❌108:119::/var/run/dbus:/bin/false
polkituser❌110:122:PolicyKit,:/var/run/PolicyKit:/bin/false
pulse❌107:116:PulseAudio daemon,:/var/run/pulse:/bin/false
将冒号指定为字段分隔符。
uniq-删除重复行
uniq执行的任务较简单,即指定一个排序过的文件,它会删除其中所有重复的行并将结果发送至标准输出。uniq经常配合sort来清楚重复输出。
命令如下:
uniq filename #去重 filename内容中的相同的行写到标准输出sort filename | uniq #对排序后的filename的内容去重写到标准输出
尽管uniq属于传统的UNIX工具,多与sort配合使用,而GNU版本的sort支持-u选项,同样能够删除有序输出中的重复行
例如对文件foo.txt进行去重
sort foo.txt | uniq
foo.txt文件内容:
a
b
c
a
b
c
命令输出结果如下:
a
b
c
从结果可以看出uniq可以去重,但是要注意uniq只能删除连续的重复行因此需要通过sort进行排序后调用uniq
常用uniq选项
选项 | 描述 |
---|---|
-c, --count | 输出重复的行,在其之前加上改行重复出现的次数 |
-d, --repeated | 只输出重复的行,不包括非重复行 |
-f n, --skip-fields=n | 忽略每行前n个字段。和sort一样,字段以空白字符分隔;但不同于sort,uniq没有能够设置其它字段分隔符的选项 |
-i, --ignore-case | 对比内容的时候忽略大小写敏感 |
-s n, --skip-chars=n | 跳过(忽略)每行开始的前n个字符 |
-u,–unique | 只输出不重复的行。忽略重复行 |
例如: 使用-c选项报告文件foo.txt中重复行的数量
命令如下:
sort foo.txt | uniq -c
命令输出结果如下:
2 a
2 b
2 c
切片和切块
接下来的命令可用于从文件中提取文本列,再以其它方式重新组合。
cut-从每行中删除部分内容
cut能够从每行提取部分文本并将其输出至标准输出。
常用的cut选项
选项 | 描述 |
---|---|
-c list, --characters=list | 提取由list定义的部分行。list可以是一个或多个逗号分隔的数值范围 |
-f list, --fields=list | 从行中提取由list定义的一个或多个字段。list可以包含一个或多个字段,抑或是逗号分隔字段范围 |
-d delim, --delimiter=delim | 如果制定了-f,则使用delim作为字段分隔符。在默认情况下,字段必须单个制表符分隔 |
–complement | 提取除-c或-f指定部分之外的文本行 |
cut适合从其他程序产生的文件中提取文本,而不是从用户输入中直接提取。
例如:利用-f选项提取由制表符分隔的第3个字段.。
用cat -A查看distros.txt文件内容:
可以看出字段间分隔符号为制表符。
使用-f选项提取字段:
cut -f 3 distros.txt
命令结果如下:
由于文件采用制表符作为分隔符,因此最好使用cut提取自字段而不是字符。
在上个例子中,提取的字段包含相同长度的数据,因此可以通过提取每行中的年份来提取字符:
命令如下:
cut -f 3 distros.txt | cut -c 7-10
命令结果如下:
再次对distros.txt执行cut,就可以提取7-10个字符,对应于日期中的年份。
在处理字段时,可以指定不同的字段分隔符。例如从/etc/passwd文件中提取第一个字段:
cut -d ':' -f 1 /etc/passwd | head
命令输出结果如下:
本例中,/etc/passwd以冒号分隔字段,cut通过-d选项指定冒号作为分隔符,通过-f选项指定第一个字段。
paste-合并行
paste将文本添加列添加到文件中。通过读取多个,然后将每个文件中的字段合并来实现的。
格式如下:
paste file1 file2 ... #按照顺序将file1,file2等文件的内容横向排列
例如:将发行日期放在发行版本名词之前,
先基于distros.txt生成几个文件:
sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt > distros-by-date.txtcut -f 1,2 distros-by-date.txt > distros-versions.txtcut -f 3 distros-by-date.txt > distros-dates.txt
其内容如下:
使用paste将发行日期放在发行版本名词之前,形成按照顺序排列的发行清单:
paste distros-dates.txt distros-versions.txt
命令输出结果如下:
join-连接两个文件中具有公共字段的行
在某些方面join和paste一样,将文本列添加到文件中,只不过前者采用了一种独特的方式。连接(join)操作常见于关系数据库,用于将多个数据表中具有共享关键字字段(shared key field)的数据合并在一起,形成所需的结果。join命令根据关键字连接多个文件的数据。
例如通过join命令将发行日期放在发行版本之前:
首先生成几个文件:
cut -f 1,1 distros-by-date.txt > distros-names.txtpaste distros-dates.txt distros-names.txt > distros-key-names.txtcut -f 2,2 distros-by-date.txt > distros-vernums.txt
paste distros-dates.txt distros-vernums.txt > distros-key-vernums.txt
文件内容如下:
现在有了两个具有共享关键字的文件。要指出的重要一点是文件必须依据关键字段排序,这样join才能正常工作:
join distros-key-vernums.txt distros-key-names.txt | head
输出结果如下:
默认情况下,join使用空白字符作为输入字段分隔符,单个空格符作为输出字段分隔符。可以指定相关选项修改规则,详见join手册页。
比较文本
比较文本文件的不同版本。对系统管理员和软件开发者而言,尤其重要。
comm-逐行比较两个已排序的文件
comm命令比较两个文本文件,显示各自独有的行和共有的行。
命令如下:
comm file1 file2 #逐行比较file1和file2,其输出结果第一列是file1独有的行,第二列是file2独有的行,第三列是file1和file2共有的行
例如:比较file1.txt和file2.txt文件
file1.txt的文件内容:
a
b
c
d
file2.txt的文件内容
b
c
d
e
使用comm比较这两个文件:
comm file1.txt file2.txt
命令输出结果如下:
可以看出a是file1独有的内容,e是
file2独有的内容,b,c,d是file1和file2共有的内容。
comm还支持-n选项,其中,n可以是1、2或3。在使用时,该选项制定了要禁止显示那些列。
例如:只显示两个文件共有的行,可以禁止输出第一列和第二列:
comm -12 file1.txt file2.txt
命令输出结果如下:
diff-逐行比较文件
diff可用于检测文件之间的差异。支持多种输出形式,能够一次处理大量文本文件。软件开发人员经常diff检测不同版本的源代码之间的差异,进而递归检查整个源代码目录(通常称为源代码树)。diff的常见用法是创建diff文件或补丁,共patch等其他程序使用。
使用diff查看上一个例子中的文件:
diff file1.txt file2.txt
命令输出如下:
其输出格式只简要描述了两个文件之间的差异。在默认格式中,每组变动之前都会有一个形如"范围 操作 范围” (range operation range)的变动命令(change command),它用于描绘将第一个文件转换为第二个文件所需的位置和变动类型。
diff的变动命令
命令 | 描述 |
---|---|
r1ar2 | 将第二个文件中位于r2处的行追加到第一个文件中的r1处 |
r1cr2 | 将第一个文件中位于r1处的行修改(替换)为第二个文件中位于r2处的行 |
r1dr2 | 删除第一个文件中位于r1处的行,该行也出现在第二个文件中r2处 |
这种格式中,范围是由逗号分隔的开头行和终止行组成的。作为默认格式(主要是为了POSIX合规性以及向后兼容传统的UNIX版本的diff),它并没有另外几种可选格式应用得那么广泛。上下文格式(context format)和合并格式(unified format)是两种更为流行的格式。
上下文格式(-c选项)如下所示:
diff -c file1.txt file2.txt
命令输出结果如下:
输出结果以两个文件的名称及其时间戳作为开头。第一个文件用星号标记,第二个文件用连字符标记。在剩下的输出结果中这两种标记分别代表对应的文件。接下来,会看到若干组变动,其中包括上下文行号。第一组中:
*** 1, 4 ***
表明第一个文件中的第1行到第4行。随后会看到:
— 1, 4 —
表明第二个文件中的第1行到第4行。
diff上下文格式的变动指示符
指示符 | 含义 |
---|---|
无 | 上下文行。并不表示两个文件之间的差异 |
- | 要删除的行。该行出现在第一个文件,但不出现在第二个文件中 |
+ | 要添加的行。该行出现在第二个文件,但不出现在第一个文件中 |
! | 要改动的行。该行的两个版本都会显示,各自出现在变动组相应的区域中 |
合并格式和上下文格式类似,但更简明。该格式由-u选项指定。
命令如下:
diff -u file1.txt file2.txt
命令输出如下:
上下文格式和合并格式之间较明显的区别在于后者去除了重复的上下文行,使合并格式的输出结果比上下文格式更为精简。可以看到和上下文格式一样的时间戳,后面紧跟着字符串@@-1,4 +1,4 @@。指明了变动组内第一个文件中的行和第二个文件中的行。接下来便是各行本身以及默认的3行上下文。
diff合并格式的变动指示符
指示符 | 含义 |
---|---|
无 | 该行为两个文件所共有 |
- | 该行要从第一个文件中删除 |
+ | 该行要加入第一个文件 |
patch-对原文件应用diff文件
patch命令能够对文本文件应用改动。它接受diff输出,一般用于将旧版本的文件转换为新版本。例如Linux内核是由一支规模庞大、组合松散的贡献者团队开发的,他们持续不断地向源代码提交细小的改动。如果某个开发人员做一次改动,就要向每位开发人员发送整个内核源代码树,这种做法并不切实际。实际上只要发送diff文件就够了。diff文件包含了内核新、旧版本之间的改动内容。接受者只需使用patch命令将这些改动应用于自己的内核源代码即可。diff/patch提供了两个重要优势。
- 相较于整个源代码树,diff文件要小得多。
- diff文件简洁地描述了所做的改动,便于查阅人员快速地对该文件做出评估。
diff/patch能够处理任何文本文件,并不局限于源代码。二者同样适用于配置文件或其他文件。
为了生成可供patch使用的diff文件,GNU文档建议按照下列方式使用diff:
diff -Naur old_file new_file > diff_file
其中,old_diff和new_diff可以是单个文件,也可以是包含多个文件的目录。-r选项能够对目录树执行递归操作。
只要生成了diff文件,就可以将diff应用于旧文件,使其成为新版本的文件:
patch < diff_file
例如将fiel1.txt的内容更改为file2.txt的内容:
生成diff_file文件:
diff -Naur file1.txt file2.txt > patchfile.txt
使用patch通过patchfile.txt文件将file1.txt的内容更新为file2.txt的内容。
patch < pachfile.txt
命令输出结果如下:
查看更改后的file1.txt的内容:
即时编辑
文本编辑器大多是交互的,也就是需要移动光标的,然后输入内容,还有非交互的文本编辑方式。
tr-转写或删除字符
tr命令可用于转写(transliterate)字符。可以将其视为某种基于字符的“搜索-替换”操作。转写是将字母表中的一个字母更改为另一个字母的过程。
命令格式如下:
tr target #
tr接受两个参数:源字符集合和目标字符集合。有3种方法表示字符集合:
- 枚举列表,例如ABCDEFGHIJKLMNOPQRSTUVWXYZ。
- 字符范围,例如A-Z。
- POSIX字符集,例如[:upper:]。
例如将多个字符转换为单个字符:
echo "lowercase letters" | tr [:lower:] A
命令输出结果如下:
除了转写,将MS-DOS文本转换为UNIX文本的问题,这需要删除每行行尾的回车符。可以通过tr命令来实现:
tr -d '\r' < dos_file > unix_file
其中dos_file是要被转换的文件,unix_file是转换后的结果。这种命令形式使用了转义字符\r来表示回车符。
tr可以使用-s选项能够"挤压"(删除)重复出现的字符。例如:
echo "aaabbbccc" | tr -s ab
命令输出结果如下:
注意重复字符必须是连续的,如果不是连续的,则不会有挤压效果:
echo "abcabcabc" | tr -s ab
命令输出结果如下:
可以看出并没有产生挤压。
sed-用于文本过滤和转换的流编辑器
sed是流编辑器(stream editor)的简称。它可以对文本流(一组指定的文件或标准输入)进行编辑。sed功能强大且比较复杂。
sed的工作方式:为其指定单个编辑命令(在命令行中)或者包含多个命令的脚本文件名,然后对文本流中的每行文本执行这些命令。下面是一个示例:
echo "front" | set 's/front/back/'
命令输出结果如下:
back
从结果可以看出这是个查找-替换操作,将front替换为back,在set的参数中s是编辑命令,front和back是s的参数,front是查找的字符串,back是替换的字符串。
可以给sed命令之前可以添加一个地址,用于指定要编辑输入流中的那些行。
例如:
[me@linuxbox ~]$ echo “front” | sed ‘1s/front/back/’
back
[me@linuxbox ~]$ echo “front” | sed ‘2s/front/back/’
第一条命令指定对第一行操作,因为输入为front,所以输出back。第二条命令无输出,是因为对第二行进行操作,但输入只有一行,故没有输出。
sed地址方式表示
地址 | 描述 |
---|---|
n | 行号,其中n为正整数 |
$ | 最后一行 |
/regexp/ | 匹配POSIX BRE的行。注意,正则表达式由正斜线分隔。正则表达式也可以选择使用其他字符分隔,这需要使用\cregxpc来指定正则表达式,其中的c就是分隔符 |
addr1,addr2 | 从addr1至addr2的行范围(包括addr1和addr2)。地址可以是前面所述的任何一种地址形式 |
first~step | 匹配从行号first开始,然后间隔依次为step的那些行。例如1~2指代所有奇数行,5~5指代第5行和之后所有是5倍数的行 |
addr1,+n | 匹配addr1和接下来的n行 |
addr! | 匹配除addr之所有行,addr可以是前面所述的任何一种地址形式 |
演示各种地址表示方式。来看地址范围:
sed -n '1,5p' distros.txt
输出结果如下:
本例中,输出范围从第1行~第5行。因此用到了p命令,改名令只是简单的输出匹配行。还必须加入-n(no auto-print选项)选项,使sed不默认输出所有行。
使用正则表达式:
sed -n '/SUSE/p' distros.txt
输出命令如下:
通过加入正斜线分隔的正则表达式/SUSE/,就能类似于grep那样的方法提取出匹配的行。
最后在地址加入!,尝试排除操作:
sed -n '/SUSE/!p' distros.txt
命令输出结果如下:
至此介绍了两个sed编辑命令s和p。
sed基本编辑命令
命令 | 描述 |
---|---|
= | 输出当其行行号 |
a | 将文本追加到前行之后 |
d | 删除当前行 |
i | 将文本插入当前行之前 |
p | 输出当前行。在默认情况下,sed会输出所有行,只编辑文件中匹配指定地址的那些行。通过-n选项,可以拒绝该默认行为 |
q | 退出sed,不再处理剩余的。如果未指定-n选项,会输出当前行 |
Q | 退出sed,不再处理剩余的行 |
s/regexp/replacement/ | 将regexp匹配的地方替换成replacement。replacement可以包含特殊字符&,其代表regexp所匹配到的文本。除此之外,replacement也可以包含序列\1~\9,其代表regexp中对应的子表达式所匹配到的文本。在后文讨论向后引用的时候,会更详细地说明。在replacement之后的结尾正斜线处,可以指定一些能够改变s命令行为的可选标志 |
y/set1/set2 | 通过将set1中的字符更换成set2中对应的字符来执行转写。注意,和tr不同的是,sed要求set1和set2这两个字符集合的长度必须相同 |
尽管日期经过了格式化,但如果采用年-月-日的格式会更好(便于排序)。通过sed命令可以实现,其命令如下:
sed 's/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/' distros.txt
命令输出如下:
sed命令的基本结构如下:
sed ‘s/regexp/replacement/’ distros.txt
因为日期采用的格式为月/日/年且出现在行尾,所以可以这样写:
[0-9]{2}/[0-9]{2}/[0-9]{4}$
该正则表达式匹配2个数位、1个斜线、2个数为、1个斜线、4个数位。regexp部分搞定。
在一些使用了ERE的应用中,包含“向后引用”特征,工作方式是这样的:如果\n出现在replacement中,这里的n是1~9中的一个数字,则此序列指代的是之前的正则表达式中对应的子表达式匹配到内容。只用将需要部分放入括号中即可:
([0-9]{2})/([0-9]{2})/([0-9]{4})$
现在得到3个子表达式。第一个包含月份,第2个包含月份中的天数,第三个包含年份。可以构建出下列replacement:
\3-\1-\2
这样就得到了年份、连字符、月份、连字符、天数。
于是整个命令如下:
set -n ‘s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/’
由于sed默认只接受BRE,因此正则表达式中一些本作为元字符的字符会被当作文字字符。解决办法是使用反斜线转义会出问题的字符:
set -n ‘s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/’ distros.txt
aspell-交互式拼写检查器
aspell多为需要拼写检查功能的程序所用,但也可以作为独立的命令行工具发挥效用。它能够智能地检查各种文本文件,其中包括HTML文档、C/C++程序、电子邮件等。
命令格式如下:
aspell check textfile
例如检查普通的文本文件:
foo.txt的文件内容:
The quick brown fox jimped over the laxy dog.
aspell check foo.txt
会提示jimped拼写错误,会被高亮显示,由于本人操作系统不支持英文拼写检查故不能进行测试。
对于HTML标签内容拼写有误,加入检查模式选项-H(HTML)就能解决这个问题:
aspell -H check foo.txt
不能测试原因同上。