由于所有类UNIX操作系统都严重依赖于文本文件来进行某些数据类型的存储,所以需要很多可以进行文本操作的工具.
常见的文本格式有
《Linux命令行大全》 [美] William E. Shotts. Jr 著 郭光伟 郝记生 译, 人民邮电出版社
如有侵权,可联系博主删除.
更多有用的Linux知识详解,可参考我的Linux学习导航页
cat我们已经在Linux命令行I/O重定向—重定向操作符、cat、管道、sort、uniq、wc、grep、head、tail、tee中讲过基本用法,下面补充三个常用的选项.
-A
选项用于显示文本中的非打印字符,例如一些控制字符—(制表符和空格),另一种常见情况是文件中包含末尾带有空格的文本行.
例如,输入制表符+The qucik brown fox jumped over the lazy dog. 按下Ctrl+D结束的如下命令
$ cat > foo.txt
The qucik brown fox jumped over the lazy dog.
得到一个以制表符开头、空格符结尾的文本行.
$ cat -A foo.txt
^IThe qucik brown fox jumped over the lazy dog.$
文本中的Tab制表符由符号"^I
“表示,意思是"Ctrl+I”.在文件末尾出现的"$
"符说明行末尾存在空格.
-n
选项用于对行编号,’-s’选项用于禁止输出多个空白行.
sort
是一个排序程序.
$ sort > foo.txt
c
b
a
$ sort foo.txt
a
b
c
sort
允许多个文件作为其参数,可以将多个文件融合成一个已经排序的整体文件。
$ sort file1.txt file2.txt file3.txt > final_sorted_list.txt
以上将三个文本文件拼接成一个已经排序好的整体文件.
表1 常见的sort选项
选项 | 全局选项表示 | 描述 |
---|---|---|
-b | –ignore-leading-blanks | 默认情况下,整个行都会进行排序操作,也就是从行的第一个字符开始.添加该选项后,sort会忽略行开头的空格,并且从第一个非空白字符开始排序. |
-f | –ignore-case | 排序时不区分大小写 |
-n | –numeric-sort | 基于字符串的长度进行排序.该选项使文件按数值顺序而不是按字母表顺序进行排序. |
-r | –reverse | 逆序排序 |
-k | –key=field1[,filed2] | 对field1与field2之间的字符排序,而不是整个文本行. |
-m | –merge | 一个排好序的文件,而不执行额外的排序操作 |
-o | –output=file | 将排序结果输出到文件而不是标准输出 |
-t | –field-separator=char | 定义字段分隔符.默认情况下,字段是由空格或制表符分开的 |
作为例子,我们给出使用du
命令+sort
命令输出home目录下前10个文件或目录占用空间最大的文件.
$ du -sh ~/* | head
16K /home/lmj/6.15
19G /home/lmj/anaconda3
13M /home/lmj/anything
29M /home/lmj/apex
16K /home/lmj/bak.bashrc.swp
19M /home/lmj/cocoapi
27M /home/lmj/code_faster
349M /home/lmj/code_tx2
809M /home/lmj/crawler
307M /home/lmj/data
我们可以使用sort -n
选项,对其所有空间占用进行排序,这里使用了-r
进行了逆序排序,即从大到小.
$ du -sh ~/* | sort -nr | head
809M /home/lmj/crawler
445M /home/lmj/trt_maskrcnn
412K /home/lmj/sampleUffMaskRCNN
396K /home/lmj/face_detect_demo
349M /home/lmj/code_tx2
313M /home/lmj/DeepFashion
307M /home/lmj/data
225M /home/lmj/log
199M /home/lmj/ubuntu18
172K /home/lmj/sampleFasterRCNN
但是这种排序仅仅对数值出现在第一行的开头有效.如果使用ls -l
命令呢?
$ ls -l ~/ | head
total 48112
total 48112
drwxrwxr-x 2 lmj lmj 4096 Jun 15 14:06 6.15
drwxrwxr-x 26 lmj lmj 4096 Apr 22 12:17 anaconda3
drwxrwxr-x 2 lmj lmj 4096 Feb 25 15:16 anything
drwxrwxr-x 11 lmj lmj 4096 Sep 25 2019 apex
-rw-r--r-- 1 lmj lmj 16384 Apr 3 10:01 bak.bashrc.swp
drwxrwxr-x 8 lmj lmj 4096 Dec 4 2019 cocoapi
drwxrwxrwx 10 lmj lmj 4096 Dec 7 2019 code_faster
drwxrwxr-x 10 lmj lmj 4096 May 3 15:37 code_tx2
drwxrwxr-x 5 lmj lmj 4096 Apr 14 2019 crawler
此时,可以用-k
选项
$ ls -l ~/ | sort -nr -k 5| head
给定一个已经排序好的文件(包括标准输入)后,uniq
会删除任何重复的行并将结果输出到标准输出中.它通常用sort结合以删除sort输出内容出重复的行.
简单的例子
$ cat > foo.txt
a
b
c
a
b
c
因为uniq 只对排好序的文本才有作用,因此直接使用没有任何效果.
$ sort foo.txt | uniq
a
b
c
表2 常见的uniq选项
选项 | 功能描述 |
---|---|
-c | 输出重复列表,并且重复行前面加上其出现次数 |
-d | 只输出重复行,但不包括单独行 |
-f n | 忽略每行前n个字段 |
-i | 行与行之间比较时忽略大小写 |
-s n | 跳过每行的前n个字符 |
-u | 仅输出不重复的行,为默认选项 |
例如
$ sort foo.txt | uniq -c
2 a
2 b
2 c
cut
命令用于从文本行中提取一段文字并将其输出至标准输出。
表3 cut选择选项
选项 | 功能描述 |
---|---|
-c char_list | 从文本行中提取char_list定义的部分内容 |
-f field_list | 从文本行中提取field_list定义的一个或多个字段 |
-d delim_char | 指定-f选项后,使用delim_char作为字段分界符号,默认为单个Tab制表符 |
–complement | 从文本中提取整行,除了那些由-c和/或-f指定的部分 |
简单的例子
$ cut -f 3 foo.txt
用于切出foo.txt中每行以制表符为分隔符的第三个字段.
例如此时我们切割出的格式为
06/01/2006
10/30/2008
03/20/2006
进一步我们可以把年份切割出来
$ cut -f 3 foo.txt | cut -c 7-10
因为固定第7-10个字符表示的是年份.
以下例子展示指定费Tab字符为制表符,从/etc/passwd文件中提取每行的第一个字符
$ cut -d ":" -f 1 /etc/passwd | head
root
daemon
bin
sys
sync
games
man
lp
mail
news
paste
是cut
的逆操作.它向文件中增加一个或是更多的文本列.该命令读取多个文件并将每个文件中提取出的字段结合为一个整体的标准输出流.
例如对于两个行数相同,且信息对应的文件 distros-dates.txt和distros-versions.txt,可以使用paste将各个参数按照指定顺序进行排列.
$ paste distros-dates.txt distros-versions.txt
join
与paste
相似,都是向文件添加列信息.不同的是,其通常与“关联数据库”联系在一起,它是一个基于共享数据库中把共享关键字段多个表格的数据组合成一个期望结果。
命令使用与patse类似,例如对于两个存在首列共享字段的文件,且按照关键字段排好序,则可以使用
$ join distros-dates.txt distros-versions.txt
comm命令一般用于文本文件之间的比较,显示两文件中相异的行以及相同的行.
首先,产生两个相近的文件
$ cat > file1.txt
a
b
c
d
$ cat > file2.txt
b
c
d
e
使用comm比较文件差异
$ comm file1.txt file2.txt
a
b
c
d
e
comm
输出三列内容,第一列显示第一个文件独有的行,第二列显示第二个文件独有的行,第三列显示两个文件共有的行。
comm
还支持-n
参数,可以省略输出第n列的内容.
diff
功能与comm
类似,但是更复杂,支持多种输出格式,并且具备一次性处理大文件集的能力。diff
通常被软件开发者用于检查不同版本的源代码之间的差异,因为其能递归检查源代码目录(通常称为源树).diff常见用法就是创建diff文件和布丁。
$ diff file1.txt file2.txt
1d0
< a
4a4
> e
默认形式的输出结果实际上是对两者文件差异的一个简洁描述.每一组改动的前面都有一个以“范围 执行操作 范围”形式(range operation range)表示的改变操作命令,该命令会告诉程序对第一个文件的某个位置进行某种改变,便可实现与第二个文件内容一致.diff改变命令如下表.
表20-4 diff改变命令
改变操作 | 功能描述 |
---|---|
r1ar2 | 将第二个文件中r2位置的行添加到第一个文件中的位置r1处 |
r1cr2 | 用第二个文件r2处的行代替第一个文件r1处的行 |
r1dr2 | 删除第一个文件r1处的行,并且删除的内容作为第二个文件r2行范围的内容 |
默认格式并没有其它格式的应用广泛,上下文格式和统一格式才是比较常用的格式.
上下文格式的输出结果
$diff -c file1.txt file2.txt
*** file1.txt 2020-06-22 11:20:05.940633105 +0800
--- file2.txt 2020-06-22 11:20:19.641086025 +0800
***************
*** 1,4 ****
- a
b
c
d
--- 1,4 ----
b
c
d
+ e
该结果以两个文件的名字和时间信息为开头,第一个文件用星号表示,第二个文件用破折号表示.输出结果的其余部分出现的星号和破折号则分别表示各自所代表的文件.其它内容便是两个文件之间的差异组,包括文件的默认行号.第一组差异,以*** 1,4 ****
开头,表示一个文件的第1行到第4行;第二组便以--- 1,4 ----
,表示第二个文件的第1行到第4行,每个差异行都是以如下四个标识符之一开头.
表5 diff上下文格式差异标识符
标识符 | 含义 |
---|---|
(无) | 该行表示上下文文本,表示两个文件共有的行 |
- | 缺少的行,表示该行仅在第一个文件中出现,第二个文件中则没有 |
+ | 多余的行,表示该行仅在第二个文件中出现,第一个文件中没有 |
! | 改变的行.两个版本的行内容都会显示出来,每一个都各自出现在差异组中相应部分 |
统一格式与上下文格式相似但是更简明,此格式用-u选项指定.
$ diff -u file1.txt file2.txt
--- file1.txt 2020-06-22 11:20:05.940633105 +0800
+++ file2.txt 2020-06-22 11:20:19.641086025 +0800
@@ -1,4 +1,4 @@
-a
b
c
d
+e
统一格式与上下文格式最显著的区别是,统一格式没有重复的文本行.@@ -1,4 +1,4 @@
字符串表示差异组描述的两个文件各自的行范围.之后每一行都以下面三种可能的标识符开头.
表6 diff统一格式的差异标识符
字符 | 含义 |
---|---|
(无) | 两个文件共有的行 |
- | 相对于第二个文件而言,,第一个文件中没有的行 |
+ | 第一个文件多余的行 |
patch
命令用于更新文本文件.它利用diff
命令的输出结果将较旧版本的文件升级成较新版本.例如在Linux内核几百万行的代码中,每个开发者仅负责小部分,对每位开发者而言,每对代码改动一次就得向其他开发者发送整个内核源代码不切实际,实际上,一般只要发送diff补丁文件即可.diff/path主要有如下优点
diff -Naur old_file new_file > diff_file
此处old_file既可以是单独的文件也可以是包含文件的目录,使用-r
参数是为了进行递归目录树搜索.
一旦创建了diff文件,便可以将其用于修补原文件old_file,从而升级为新文件new_file.
$ diff -Naur file1.txt file2.txt > patchfile.txt
$ patch < patchfile.txt
patching file file1.txt
$ cat file1.txt
b
c
d
e
之前描述的文本编辑大多是交互式的,也就是说只要手动移动鼠标然后输入需要进行的改变.然而,也可以进行非交互的方式进行文本编辑.
tr
是替换字符命令,可以将其看做基于字符的查找和替换操作.
下例是使用tr进行大小写字母替换的例子
$ echo "lowercase letters" | tr a-z A-Z
LOWERCASE LETTERS
也就是说,tr
可以对标准输入进行操作并且将结果以标准形式输出.tr
有两个参数:等待转换的字符集和与之对应的替换字符集.字符集的表示方式有
多数情况下,两个字符集应该是同等长度;然而,第一个字符集比第二个字符集长也是有可能的,例如
$ echo "lowercase letters" | tr [:lower:] A
AAAAAAAAA AAAAAAA
tr还有个有趣的用法,使用-s
选项,可以“挤兑”(删除)重复出现的字符,示例如下
$ $ echo "aabbcc" | tr -s ab
abcc
请注意,重复的字符必须是毗邻的,否则不起作用
sed
是stream editor(流式编辑器)的缩写,它可以对文本流、指定文件集或标准输入进行文本编辑.
sed
的用法,总的来说,首先给定sed某个简单的编辑命令(在文本行中)或是包含多个命令的脚本文件名,然后sed便对文本流的内容执行给定的编辑命令.例如
$ echo "front" | sed "s/front/back/"
back
首先echo生成了只包含一个单词的文本流,然后将该文本流叫给sed处理,而sed对其执行s/front/back/指令,最后输出"back".可以认为这里sed与vi中的替换命令相似.
sed的命令以单个字母开头,上个例子中,以字母s表示替代,气候紧跟替换字符,替换字符由分界符的斜线字符分开.分界符选择任意,习惯上使用斜线.
sed中国的多数命令允许在其前添加一个地址,该地址用来指定输入流的哪一行被编辑.如果地址省略,则默认对第一行编辑.最简单的地址就是行号.
$ echo "front" | sed "1s/front/back/"
back
表7 sed地址表示法
地址 | 功能说明 |
---|---|
N | n是正整数表示行号 |
$ | 最后一行 |
/regexp/ | 用POSIX基本表达式描述的行 |
addr1,addr2 | 行范围,从addr1至addr2的所有行 |
first~step | 代表从行号为first开始,以step为间隔的所有行.例如1~2为所有奇数行 |
addr1,+n | addr1行及其之后的n行 |
addr! | 除了addr行之外的所有行,addr可以是以上任意表示形式 |
例如
$ sed -n '1,5p' distros.txt
p
命令代表输出指定行,这里输出了第1行到第5行,-n选项是不会自动打印选项,以防sed会默认输出每一行内容.
$ sed -n '/SUSE/p' distros.txt
以上是使用正则表达式搜索匹配道SUSE的行.
我们已经学了s和p选项,sed更完整的基本编辑指令如下
表8 sed基本编辑指令表
命令 | 功能描述 |
---|---|
= | 输出当前行号 |
a | 在当前行后附加文本 |
d | 删除当前行 |
i | 在当前行前输入文本 |
p | 打印当前行.默认情况下,sed会输出每一行并且只编辑文件内那些匹配指定地址的行,使用-n选项后,默认操作被覆盖. |
q | 退出sed不再处理其他行,如果没有指定-n选项,就会输出当前行 |
Q | 直接退出sed不再处理行 |
s/regexp/replacement/ | 将regexp的内容替换为replacement代表的内容 |
y/set1/set2 | 将字符集set1转换为字符集set2,与tr不同,sed要求这两个字符集等长. |
检查一篇简单散文的拼写错误,可以用如下方式
aspell check textfile
其检验模式是与用户交互的,被怀疑的错误字符以高亮显示,0~9显示拼写建议,还有一些其它操作.
首先,创建含拼写错误的txt,然后检查它
$ cat > foo.txt
The quick brown jimped over the laxy dog
$ aspell check foo.txt