19. 文本处理

[TOC]

文本应用程序

cat 连接文件并且打印到标准输出

  • -A 选项,在文本中显示非打印字符。
$ cat -A foo.txt 
   The quick brown fox jumped over the lazy dog.$
  • -n 选项,给文本行添加行号。
  • -s 选项,禁止输出多个空白行。
cat > foo.txt
The quick brown fox

jump over the lazy dog.
^C
$ cat -ns foo.txt 
     1  The quick brown fox
     2
     3  jump over the lazy dog.

sort 给文本行排序

sort 程序对标准输入的内容,或命令行中指定的一个或多个文件进行排序,然后把排序结果发送到标准输出。

$ sort > foo.txt
c
b
a
# Ctrl+D 表示文件的结尾。

$ cat foo.txt 
a
b
c
常见的 sort 选项 长选项 描述
-b --ignore-leading-blanks 默认情况下,对整行进行排序,从每行的第一个字符开始。这个选项导致 sort 程序忽略 每行开头的空格,从第一个非空白字符开始排序。
-f --ignore-case 让排序不区分大小写。
-n --numeric-sort 基于字符串的长度来排序。使用此选项允许根据数字值执行排序,而不是字母值。
-r --reverse 按相反顺序排序。结果按照降序排列,而不是升序。
-k --key=field1[,field2] 对从 field1 到 field2 之间的字符排序,而不是整个文本行。看下面的讨论。
-m --merge 把每个参数看作是一个预先排好序的文件。把多个文件合并成一个排好序的文件,而没有执行额外的排序。
-o --output=file 把排好序的输出结果发送到文件,而不是标准输出。
-t --field-separator=char 定义域分隔字符。默认情况下,域由空格或制表符分隔。

示例一:

# 显示前10个文件,默认按路径名排序
$ du -s /root/* | head
75716   /root/Agora_Recording
31420   /root/Agora_Recording_SDK_for_Linux_v2_2_2_FULL.tar.gz
4       /root/foo.txt
65728   /root/nodejs-v8.10.0
64136   /root/p2p
36456   /root/paxos
13692   /root/PaxosUdp_let
114528  /root/t-blockchain_screen
28      /root/testDocument

#  -nr 选项,反向数值排序,最大数值排列在第一位。
# 这种排序起作用是因为数值出现在每行的开头。
$ du -s /root/* | sort -nr | head
114528  /root/t-blockchain_screen
75716   /root/Agora_Recording
65728   /root/nodejs-v8.10.0
64136   /root/p2p
36456   /root/paxos
31420   /root/Agora_Recording_SDK_for_Linux_v2_2_2_FULL.tar.gz
13692   /root/PaxosUdp_let
28      /root/testDocument
4       /root/foo.txt

示例二(基于某个字段排序):

$ ls -l /usr/bin | head
total 431444
-rwxr-xr-x 1 root   root       51920 Mar  3  2017 [
-rwxr-xr-x 1 root   root          96 Nov 13 00:18 2to3-3.5
-rwxr-xr-x 1 root   root       22696 Sep 28 06:10 aa-enabled
-rwxr-xr-x 1 root   root       15072 Apr  9  2016 acpi_listen
-rwxr-xr-x 1 root   root        6356 Dec 19 01:08 add-apt-repository
-rwxr-xr-x 1 root   root       18888 May 16  2018 addpart
lrwxrwxrwx 1 root   root          26 Aug 30 21:39 addr2line -> x86_64-linux-gnu-addr2line
-rwxr-xr-x 1 root   root        2555 May 24  2018 apport-bug
-rwxr-xr-x 1 root   root       13347 May 24  2018 apport-cli

# -nr 选项,执行相反的数值排序
# -k 5,让 sort 程序使用第五字段作为排序的关键值。
# 空白字符(空格和制表符)被当作是字段间的界定符。
$ ls -l /usr/bin | sort -nr -k 5 | head
-rwxr-xr-x 1 root   root    22889264 Oct 23 20:11 mysql_embedded
-rwxr-xr-x 1 root   root    18929936 Jul 19  2018 snap
-rwxr-xr-x 1 root   root    14664464 Aug 11  2015 mongod
-rwxr-xr-x 1 root   root    14537488 Aug 11  2015 mongorestore
-rwxr-xr-x 1 root   root    14537488 Aug 11  2015 mongofiles
-rwxr-xr-x 1 root   root    14508816 Aug 11  2015 mongodump
-rwxr-xr-x 1 root   root    14504720 Aug 11  2015 mongostat
-rwxr-xr-x 1 root   root    14504720 Aug 11  2015 mongoimport
-rwxr-xr-x 1 root   root    14500624 Aug 11  2015 mongoexport
-rwxr-xr-x 1 root   root    14496528 Aug 11  2015 mongooplog

# 多键值排序语法
# 1,1,“始于并且结束于第一个字段。”
# 2n,第二个字段是排序的键值,并且按照数值排序。
# b(忽略开头的空格),n(数值排序),r(逆向排序)
$ sort --key=1,1 --key=2n distros.txt

#  -k 3.7,指示 sort 程序使用一个排序键值,其始于第三个字段中的第七个字符,对应于年的开头。
# 指定 -k 3.1 和 -k 3.4 来分离日期中的月和日。
# b 选项用来删除日期字段中开头的空格
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

uniq 报告或者省略重复行

当给定一个排好序的文件(包括标准输出),uniq 会删除任意重复行,并且把结果发送到标准输出。它常常和 sort 程序一块使用,来清理重复的输出。

uniq 程序是一个传统的 Unix 工具,经常与 sort 程序一块使用, 但是这个 GNU 版本的 sort 程序支持一个 -u 选项,其可以从排好序的输出结果中删除重复行。

uniq 程序能完成任务,其输入必须是排好序的数据,因为 uniq 只会删除相邻的重复行。

# 新建一个包含重复的测试文件
$ cat > foo.txt
a
b
c
a
b
c
^C
 
# 数据没有排序好,输出不变:
$ uniq foo.txt
a
b
c
a
b
c

# 数据经过 sort 排序,uniq 才会删除重复项:
$ sort foo.txt | uniq
a
b
c

$ sort foo.txt | uniq -c
      2 a
      2 b
      2 c
常用的 uniq 选项 说明
-c 输出所有的重复行,并且每行开头显示重复的次数。
-d 只输出重复行,而不是特有的文本行。
-f n 忽略每行开头的 n 个字段,字段之间由空格分隔,正如 sort 程序中的空格分隔符;然而,不同于 sort 程序,uniq 没有选项来设置备用的字段分隔符。
-i 在比较文本行的时候忽略大小写。
-s n 跳过(忽略)每行开头的 n 个字符。
-u 只是输出独有的文本行。这是默认的。

cut 从每行中删除文本区域

cut 程序被用来从文本行中抽取文本,并把其输出到标准输出。
cut 命令最好用来从其它程序产生的文件中抽取文本,而不是从人们直接输入的文本中抽取。

选项 说明
-c char_list 从文本行中抽取由 char_list 定义的文本。这个列表可能由一个或多个逗号 分隔开的数值区间组成。
-f field_list 从文本行中抽取一个或多个由 field_list 定义的字段。这个列表可能 包括一个或多个字段,或由逗号分隔开的字段区间。
-d delim_char 当指定 - f 选项之后,使用 delim_char 做为字段分隔符。默认情况下, 字段之间必须由单个 tab 字符分隔开。
--complement 抽取整个文本行,除了那些由 - c 和/或 - f 选项指定的文本。
# -f 3,抽取第三段
# -c 7-10,抽取从位置 7 到 10 的字符
$ cut -f 3 distros.txt | cut -c 7-10

# -d 选项,指定冒号做为字段分隔符。
$ cut -d ':' -f 1 /etc/passwd | head
root
daemon
bin
sys
sync
games
man
lp
mail
news

paste 合并文件文本行

paste 命令的功能正好与 cut 相反。它会添加一个或多个文本列到文件中,而不是从文件中抽取文本列。
它通过读取多个文件,然后把每个文件中的字段整合成单个文本流,输入到标准输出。

join 基于某个共享字段来联合两个文件的文本行

一个 join 操作通常与关系型数据库有关联,在关系型数据库中来自多个享有共同关键域的表格的数据结合起来,得到一个期望的结果。

默认情况下,join 命令使用空白字符做为输入字段的界定符,一个空格作为输出字段的界定符。这种行为可以通过指定的选项来修改。

比较文本

comm 逐行比较两个有序的文件

comm 程序会比较两个文本文件,并且会显示每个文件特有的文本行和共有的文本行。

  • comm 命令产生三列输出。第一列包含第一个文件独有的文本行;第二列文本行是第二列独有的;第三列包含两个文件共有的文本行。
  • -n 选项,这里 n 代表 1,2 或 3。这些选项使用的时候,指定了要隐藏的列。
# 只显示两个文件共有的行
$ comm -12 file1.txt file2.txt

diff 逐行比较文件

diff 程序被用来监测文件之间的差异。然而,diff 是一款更加复杂的工具,它支持许多输出格式,并且一次能处理许多文本文件。软件开发员经常使用 diff 程序来检查不同程序源码版本之间的更改,diff 能够递归地检查源码目录,经常称之为源码树。diff 程序的一个常见用例是创建 diff 文件或者补丁,它会被其它程序使用,例如 patch 程序,来把文件从一个版本转换为另一个版本。

diff 更改命令 说明
r1ar2 把第二个文件中位置 r2 处的文件行添加到第一个文件中的 r1 处。
r1cr2 用第二个文件中位置 r2 处的文本行更改(替代)位置 r1 处的文本行。
r1dr2 删除第一个文件中位置 r1 处的文本行,这些文本行将会出现在第二个文件中位置 r2 处。
diff 统一模式更改指示符 含义
空格 两个文件都包含这一行。
- 在第一个文件中删除这一行。
+ 添加这一行到第一个文件中。

patch 逐行比较文件

patch 程序被用来把更改应用到文本文件中。它接受从 diff 程序的输出,并且通常被用来把较老的文件版本转变为较新的文件版本。

使用 diff/patch 组合提供了两个重大优点:

  1. 与整个源码树的大小相比较而言,一个 diff 文件非常小。
  2. 一个 diff 文件简洁地显示了所做的修改,从而允许程序补丁的审阅者能快速地评估它。

运行时编辑

使用非交互式的方法来编辑文本。例如:通过单个命令把一系列修改应用到多个文件中。

tr 翻译或删除字符

tr 程序被用来更改字符。可以把它看作是一种基于字符的查找和替换操作。

示例:

# 小写转大写
$ echo "lowercase letters" | tr a-z A-Z
LOWERCASE LETTERS

# 查看 tr 命令所支持地完整的转义序列和字符类别列表
$ tr --help

tr 命令接受两个参数:要被转换的字符集以及相对应的转换后的字符集。字符集可以用三种方式来表示:

  1. 一个枚举列表。例如,ABCDEFGHIJKLMNOPQRSTUVWXYZ。
  2. 一个字符域。例如,A-Z 。注意这种方法有时候面临与其它命令相同的问题,归因于语系的排序规则,因此应该谨慎使用。
  3. POSIX 字符类。例如,[:upper:]。

大多数情况下,两个字符集应该长度相同;然而,有可能第一个集合大于第二个,尤其如果我们想要把多个字符转换为单个字符

# 
$ echo "lowercase letters" | tr [:lower:] A
AAAAAAAAA AAAAAAA

除了换字之外,tr 命令能允许字符从输入流中简单地被删除。

# MS-DOS 文本文件转换为为 Unix 文本文件时。需要删除每行末尾的回车符。
# dos_file 是需要被转换的文件
# unix_file 是转换后的文件
$ tr -d '\r' < dos_file > unix_file

# 使用 - s 选项,tr 命令能 “挤压”(删除)相邻的重复的字符实例:
$ echo "aaabbbccc" | tr -s ab
abccc

sed 用于筛选和转换文本的流编辑器

  • sed 是 stream editor(流编辑器)的简称,它对文本流进行编辑。
  • sed 是一款强大的,并且有些复杂的程序(有整本内容都是关于 sed 程序的书籍)。
  • sed 的工作方式是要不给出单个编辑命令(在命令行中)要不就是包含多个命令的脚本文件名,然后它就按行来执行这些命令。

格式:sed 's/regexp/replacement/' file...

# 将 front 替换为 back
# s:替换命令
$ echo "front" | sed 's/front/back/'
back
sed 地址表示法 说明
n 行号,n 是一个正整数。
$ 最后一行。
/regexp/ 所有匹配一个 POSIX 基本正则表达式的文本行。注意正则表达式通过 斜杠字符界定。选择性地,这个正则表达式可能由一个备用字符界定,通过 \ cregexpc 来 指定表达式,这里 c 就是一个备用的字符。
addr1,addr2 从 addr1 到 addr2 范围内的文本行,包含地址 addr2 在内。地址可能是上述任意 单独的地址形式。
first~step 匹配由数字 first 代表的文本行,然后随后的每个在 step 间隔处的文本行。例如 1~2 是指每个位于偶数行号的文本行,5~5 则指第五行和之后每五行位置的文本行。
addr1,+n 匹配地址 addr1 和随后的 n 个文本行。
addr! 匹配所有的文本行,除了 addr 之外,addr 可能是上述任意的地址形式。
sed 基本编辑命令 说明
= 输出当前的行号。
a 在当前行之后追加文本。
d 删除当前行。
i 在当前行之前插入文本。
p 打印当前行。默认情况下,sed 程序打印每一行,并且只是编辑文件中匹配 指定地址的文本行。通过指定 - n 选项,这个默认的行为能够被忽略。
q 退出 sed,不再处理更多的文本行。如果不指定 - n 选项,输出当前行。
Q 退出 sed,不再处理更多的文本行。
s/regexp/replacement/ 只要找到一个 regexp 匹配项,就替换为 replacement 的内容。 replacement 可能包括特殊字符 &,其等价于由 regexp 匹配的文本。另外, replacement 可能包含序列 \1 到 \9,其是 regexp 中相对应的子表达式的内容。更多信息,查看 下面 back references 部分的讨论。在 replacement 末尾的斜杠之后,可以指定一个 可选的标志,来修改 s 命令的行为。
y/set1/set2 执行字符转写操作,通过把 set1 中的字符转变为相对应的 set2 中的字符。 注意不同于 tr 程序,sed 要求两个字符集合具有相同的长度。

喜欢 sed 的人们也会喜欢。。。

sed 是一款非常强大的程序,它能够针对文本流完成相当复杂的编辑任务。它最常用于简单的行任务,而不是长长的脚本。
许多用户喜欢使用其它工具,来执行较大的工作。 在这些工具中最著名的是 awkperl。它们不仅仅是工具,像这里介绍的程序,且延伸到完整的编程语言领域。
特别是 perl,经常被用来代替 shell 脚本,来完成许多系统管理任务,同时它也是一款非常流行网络开发语言。
awk 更专用一些。其具体优点是其操作表格数据的能力。awk 程序通常逐行处理文本文件,这点类似于 sedawk 使用了一种方案,其与 sed 中地址之后跟随编辑命令的概念相似。
虽然关于 awkperl 的内容都超出了本书所讨论的范围, 但是对于 Linux 命令行用户来说,它们都是非常好的技能。

aspell 交互式拼写检查器

aspell 是一款交互式的拼写检查器。

拼写检查一个包含简单的文本文件:

$ aspell check textfile

示例:

# 生成带错误单词的文件
$ cat > foo.txt
The queck brown fox jimped over the laxy dog.

# 交互式拼写检查
$ aspell check foo.txt

# 拼写可疑且高亮显示的单词,下方会有提示符
1) quack                                                            6) QC
2) quick                                                            7) Que
3) Keck                                                             8) gecko
4) quark                                                            9) kick
5) quirk                                                            0) quake
i) Ignore                                                           I) Ignore all
r) Replace                                                          R) Replace all
a) Add                                                              l) Add Lower
b) Abort                                                            x) Exit

除非由命令行选项 --dont-backup 告诉 aspell,否则通过追加扩展名 .bak 到文件名中, aspell 会创建一个包含原始文本的备份文件。

-rw-r--r--  1 root    root        45 Jan 19 17:26 foo.txt
-rw-r--r--  1 root    root        46 Jan 19 17:24 foo.txt.bak

aspell 会认为 HTML 标志的内容是拼写错误。通过包含 -H(HTML)检查模式选项,这个问题能够解决:

# 检查 HTML 文件:
$ aspell -H check foo.txt

注意:默认情况下,aspell 会忽略文本中 URL 和电子邮件地址。通过命令行选项,可以重写此行为。也有可能指定哪些标志进行检查及跳过。

其他操作命令

一些有趣的文本操作命令也非常值得研究。如:split(把文件分割成碎片), csplit(基于上下文把文件分割成碎片),和 sdiff(并排合并文件差异)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容