引言:对于文本操作来说,除了剪切和粘贴外还有更多的操作,尤其是不使用GUI的时候,这更明显。在本文中,作者讲解了使用GUN文本工具包来进行文本处理。学完本文,你将会像专家一样处理文本。
概述:本文介绍了“过滤器”,可以使用过滤器构建复杂的管道来处理文本。你将学会如何显示文本、排序、单词和行统计、转换等多种操作技术。你还会学习使用sed编辑器。具体入下:
[root@localhost ~]# echo -e "apple\npear\nbanana" | sort
apple
banana
pear
[root@localhost ~]#
每个命令都可能有选项或者参数。你同样可以使用 | 来把第二个命令的输出作为第三个命令的输入,以此类推。通过构建长的命令管道(每个命令能力有限)来完成任务是Linux/Unix中常见的方法。 有时候,你可能会看到一个命令的参数是 - 而不是一个文件名,这个-的含义是此命令的输入来自标准输入流而不是文件。
[root@localhost ~]# mkdir lpi103-2
[root@localhost ~]# cd lpi103-2/
[root@localhost lpi103-2]# echo -e "1 apple\n2 pear\n3 banana" > text1
[root@localhost lpi103-2]# cat text1
1 apple
2 pear
3 banana
如果不给cat命令提供参数,那么它将会把标准输入流作为其输入。这中特性和输出重定向配合就可以创建另一个文件,如下:
cat >text2
9 plum
3 banana
10 apple
[root@localhost lpi103-2]# cat text*
1 apple
2 pear
3 banana
9 plum
3 banana
10 apple
注意,上面两个文件输出对齐的不同。要弄清楚其中的原因,你需要查看文件里的控制字符。控制字符用来控制输出效果,本身并不显示自身,所以我们需要以一种格式dump文件来找出和解释这些特殊的字符。GUN工具包里的od命令可以完成这个任务。-A 选项指定字符位置的计数法,可以是x(十六进制),d(十进制),o(八进制)或者n(不显示位置)。-t 用来指定显示的方式,c是转义字符的方式,a则是名字方式。
[root@localhost lpi103-2]# od text2
0000000 004471 066160 066565 031412 061011 067141 067141 005141
0000020 030061 060411 070160 062554 000012
0000031
[root@localhost lpi103-2]# od -A d -t c text2
0000000 9 \t p l u m \n 3 \t b a n a n a \n
0000016 1 0 \t a p p l e \n
0000025
[root@localhost lpi103-2]# od -A n -t a text2
9 ht p l u m nl 3 ht b a n a n a nl
1 0 ht a p p l e nl
我们的示例文件都很小,当遇到大文件时就需要把它分割成多个小的分片。例如,你可能需要把一个大文件分割成CD-大小的分片,这样才能把它刻录成CD盘。split命令用来完成分割任务,并且cat可以很容易地把分割的分片重新连接成原来的大文件。默认情况下,split分割出的小文件的文件名格式为: xaa, xab,....。当然你可以通过命令行选项来改变默认值,也可以改变小文件的大小以及是否按行分割或者按字符分割。
[root@localhost lpi103-2]# split -l 2 text1 # 按行分割,每2行一个文件片
[root@localhost lpi103-2]# split -b 17 text2 y # 按字符数分割,每个文件片17个字符
[root@localhost lpi103-2]# cat yaa
9 plum
3 banana
1[root@localhost lpi103-2]# cat y* x*
9 plum
3 banana
10 apple
1 apple
2 pear
3 banana
注意yaa文件片没有以换行结束,所以输出它以后,我们的bash提示符偏移了。
[root@localhost lpi103-2]# ls -l text*
-rw-r--r-- 1 root root 24 May 7 14:29 text1
-rw-r--r-- 1 root root 25 May 7 14:48 text2
[root@localhost lpi103-2]# wc text*
3 6 24 text1
3 6 25 text2
6 12 49 total
wc提供了其他的选项来控制输出格式或者输出其他的信息,如最大行的长度,具体参看man手册页。
root@localhost lpi103-2]# dmesg | tail -n15 | head -n 6 # 先从倒数15行开始到结束,然后是取前6行
tg3 0000:02:00.0: eth0: Link is up at 100 Mbps, full duplex
tg3 0000:02:00.0: eth0: Flow control is on for TX and on for RX
tg3 0000:02:00.0: eth0: Link is down
tg3 0000:02:00.0: eth0: Link is up at 100 Mbps, full duplex
tg3 0000:02:00.0: eth0: Flow control is off for TX and off for RX
Bluetooth: Core ver 2.15
[root@localhost lpi103-2]# expand -t 1 text2 # 把一个Tab替换成一个空格
9 plum
3 banana
10 apple
[root@localhost lpi103-2]# expand -t 8 text2 | unexpand -a -t2 | expand -t3 # 把一个Tab替换成8个空格,再把每2个空格替换成一个Tab, 在把每个Tab替换成3个空格
9 plum
3 banana
10 apple
不幸的是,你无法使用unxpand把text1中的空格替换成Tab。因为unexpand至少需要2个空格才能换成Tab。不过,你可以使用tr命令来完成这种替换。因为tr就是一个单纯的过滤器,你可以使用cat命令为其提供输入。
[root@localhost lpi103-2]# cat text1 | tr ' ' '\t' | cat - text2
1 apple
2 pear
3 banana
9 plum
3 banana
10 apple
如果想知道到底发生了怎样的替换,可以使用od -ta来查看。
[root@localhost lpi103-2]# nl text2 | pr -m - text1 | head
2013-05-07 16:04 Page 1
1 9 plum 1 apple
2 3 banana 2 pear
3 10 apple 3 banana
[ian@echidna lpi103-2]$ echo "This is a sentence. " !#:* !#:1->text3
echo "This is a sentence. " "This is a sentence. " "This is a sentence. ">text3
[ian@echidna lpi103-2]$ echo -e "This\nis\nanother\nsentence.">text4
[ian@echidna lpi103-2]$ cat -et text3 text4
This is a sentence. This is a sentence. This is a sentence. $
This$
is$
another$
sentence.$
[ian@echidna lpi103-2]$ fmt -w 60 text3 text4
This is a sentence. This is a sentence. This is a
sentence.
This is another sentence.
[root@localhost lpi103-2]# cat text1 | tr ' ' '\t' | sort - text2
10 apple
1 apple
2 pear
3 banana
3 banana
9 plum
[root@localhost lpi103-2]# cat text1 | tr ' ' '\t' | sort -u -k1n -k2 - text2
1 apple
2 pear
3 banana
9 plum
10 apple
请注意,10排到了最前面,因为默认是按照字母顺序排序的。庆幸的是,sort支持数字和字母两种排序方式,而且每一个列排序的方式还可以不同。默认各个列是根据Tab或者空格分隔的。
[root@localhost lpi103-2]# cat text1 | tr ' ' '\t' | sort -k2 - text2 | uniq -f1 # 忽略第一个字段
10 apple
3 banana
2 pear
9 plum
[root@localhost lpi103-2]# cut -f1-2 --output-delimiter=' ' text2 # 抽取两个字段,并在输出时字段之间使用空格分开
9 plum
3 banana
10 apple
[root@localhost lpi103-2]# paste text1 text2
1 apple 9 plum
2 pear 3 banana
3 banana 10 apple
这只是paste最简单的用法,更多复杂的用法,请参考man手册页。
[root@localhost lpi103-2]# sort -k2 text2 | join -1 2 -2 2 text5 -
apple 1 10
banana 3 3
[root@localhost lpi103-2]# sed 's/a/A/' text1
1 Apple
2 peAr
3 bAnana
[root@localhost lpi103-2]# sed 's/a/A/g' text1
1 Apple
2 peAr
3 bAnAnA
[root@localhost lpi103-2]# sed '2d;$s/a/A/g' text1
1 apple
3 bAnAnA
[root@localhost lpi103-2]# sed -e '2,${' -e 's/a/A/g' -e '}' text1
1 apple
2 peAr
3 bAnAnA
[root@localhost lpi103-2]# sed -e '/pear/,/bana/{' -e 's/a/A/g' -e '}' text1
1 apple
2 peAr
3 bAnAnA
[root@localhost lpi103-2]# sed -e '/pear/,/bana/{s/a/A/g}' text1
1 apple
2 peAr
3 bAnAnA
[root@localhost lpi103-2]# echo -e "s/ /\t/g">sedtab
[root@localhost lpi103-2]# cat sedtab
s/ / /g
[root@localhost lpi103-2]# sed -f sedtab text1
1 apple
2 pear
3 banana
[root@localhost lpi103-2]# sed '=' text2
1
9 plum
2
3 banana
3
10 apple
[root@localhost lpi103-2]# sed '=' text2 | sed 'N;s/\n//'
19 plum
23 banana
310 apple
[root@localhost lpi103-2]# cat text1 text2 text1 text2 >text6
[root@localhost lpi103-2]# cat text6
1 apple
2 pear
3 banana
9 plum
3 banana
10 apple
1 apple
2 pear
3 banana
9 plum
3 banana
10 apple
[root@localhost lpi103-2]# ht=$(echo -en "\t")
[root@localhost lpi103-2]# echo $ht
[root@localhost lpi103-2]# sed '=' text6 | sed "N
> s/^/ /
> s/^.*\(......\)\n/\1$ht/"
1 1 apple
2 2 pear
3 3 banana
4 9 plum
5 3 banana
6 10 apple
7 1 apple
8 2 pear
9 3 banana
10 9 plum
11 3 banana
12 10 apple
[root@localhost lpi103-2]# sed --version
GNU sed version 4.2.1
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,
to the extent permitted by law.
GNU sed home page: .
General help using GNU software: .
E-mail bug reports to: .
Be sure to include the word ``sed'' somewhere in the ``Subject:'' field.