所谓的文本处理是指对文本进行查找、替换、删除、排序等操作, linux在文本处理方面提供了大量优秀的工具, 使得在linux下进行文本处理极其的方便.
我们平常的工作中, 经常会用到文本处理, 比如日志分析, 比如文本抽取, 等等, 所以掌握好文本处理, 将会对我们的工作起到极大的作用.
下面我就来逐个介绍下这些强大的工具, 对于我觉得大家可能比较熟知的工具及用法, 我会略过, 或者粗讲下.
Linux哲学中, 为了更好的组合各种命令达到更加强大的功能, 大多数文本处理命令的输入既可以是文件, 也可以是标准输入, 如果没有指定输入文件, 则默认从标准输入读数据. 输出都是标准输出, 方面传给管道线的下一个命令, 想要输出到文件的话, 重定向下即可. 下面介绍的这些命令, 如无特殊说明, 则都可以从文件或者标准输入读入数据.
输入为命令行参数
非常常用的命令, 主要用作输出字符串. 如果只是为了向管道线的下一个命令传输入的话, 可以使用Here String:
echo xxx | md5sum md5sum <<< xxx
后者速度上应该会快一点, 不需要经过管道.
echo -e '\033[1;31mHello, \033[0m\033[1;33mworld!\033[0m'
Hello, world!
这个工具可以更方便的输出ANSI颜色.
输入为命令行参数
更强大的输出你想要的文本的命令, 类似C里面的printf
printf '\033[1;31m%s, \033[0m\033[1;33m%d\033[0m and \u4e2d\u6587!\n' "Hello" 34
Hello, 34 and 中文!
不过此命令较echo来说, 使用率会低很多, 大多数情况下echo就能搞定了.
输入为命令行参数
不停的输出字符串STRING, 默认是y. 这个命令用处比较少, 但会有用, 比如测试tail命令.
cat file | grep xxx grep xxx file
前者相比后者多启动了一个进程, 还经过了管道. file很大的话, 性能差距很容易就看出来了.
taoshanwen@taoshanwen-laptop ~$ echo -e '\r' | cat -v
^M
tac | rev
非常常用的命令, 打印文本中匹配模式的行, 下面的选项最好都能掌握.
grep [OPTIONS] PATTERN [FILE...] grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
grep -P "(?i)AB"
(require 'coding-settings) ("C-x U" revert-buffer-with-coding-system-no-confirm-sb))) ("C-x M-C" set-buffer-file-coding-system))) (set-buffer-file-coding-system 'unix)) (set-buffer-file-coding-system 'dos))
有时候想看看匹配行周围都是啥, 下面这几个选项就非常有用了:
grep -F
grep -E
grep -r
grep的模糊匹配版本
对压缩文件进行grep, 接受的选项和grep完全一样
对结构化的文本, 如SGML、XML、HTML进行搜索、抽取, 功能非常强大
类似agrep
最主要的用途就是统计行数
打印文本的md5, 主要用作文件校验, 防止文件传输时发生错误或者被篡改. -c选项检查md5是否正确
非常常用的命令, 啥序都能排
F[.C][OPTS]
其中, F是字段号, C是字符号, OPTS是排序选项, 可以每个字段排序的规则不一样. F, C都是从1开始
sort -t ' ' -k1,1d -k2.2,2n <<-EOF bb 113 aa 224 cc 323 dd 444 cc 513 EOF aa 224 bb 113 cc 513 cc 323 dd 444
du -sh * | sort -h
LANG Used to determine the locale category for any category not specifically selected with a variable starting with LC_. LC_ALL This variable overrides the value of LANG and any other LC_ variable specifying a locale category. LC_COLLATE This variable determines the collation order used when sorting the results of pathname expansion, and determines the behavior of range expressions, equivalence classes, and collating sequences within pathname expansion and pattern matching.
所以, sort时, 设置LC_ALL是最保险的做法.
拓朴排序, 该命令可能会用的比较少
tsort <<EOF a b c d e f b c d e EOF
输出:
a b c d e f
也是非常常用的一个命令. 这个命令主要用来对有序序列进行去重, 所以它常和sort联合起来使用, 但是sort -u本身就有去重的功能, 所以当你仅仅只是为了去重时, sort -u就可以帮你搞定了(当输入文本巨大时, 可以用hash来去重提高性能, 比如awk的关联数组), 所以呢, 当年需要对重复的数据进行统计时, 会用到uniq. 当然其实uniq相比sort -u而言, 对重复数据有更加强大的处理
sort A B | uniq
逐行比较两个有序文件, 分三列输出文件1独有的行、文件2独有的行、文件12共有的行,
$ cat ab ax by cz $ cat ac ax bd cz $ comm ab ac ax bd by cz
如果sort -R产生的结果还不够乱的话, 我想这个命令应该就是你需要的了. 该命令产生完全乱序的结果, 而且速度应该比sort -R快(shuf不用排序), 还有功能更强大
挺常用的一个命令, 能非常方便的取某个字段
列表可以有多个, 之间以逗号分割, 比如:
cut -f1-3,4-7
这个命令很有意思, 把两个文件按行粘贴到一块, 曾经我想自己写个程序搞定这个需求, 后来发现linux下竟然已经有这玩意了(linux总能给你带来惊喜)
$ cat num2 1 2 $ cat let3 a b c $ paste num2 let3 1 a 2 b c
$ paste -s num2 let3 1 2 a b c
这是一个稍微高级点的命令, 它把输入文件当成一个key/value对, 然后会把同一个key的所有value粘贴到一块, 来个例子:
$ cat file1 a 1 b 2 c 3 $ cat file2 a 4 c 6 $ join file1 file2 a 1 4 c 3 6
join默认把第一额字段当作key, 字段之间以空格分割, 作为key的字段必须有序.
每个元素之间以逗号或者空格分割
主要对文本中的字符进行替换、删除.
tr [OPTION]... SET1 [SET2]
字符集合可以由一系列的字符构成, 也可以具有以下形式:
当提供2个字符集合时, 表示把SET1中的字符替换成SET2中的对应的字符, 比如:
tr a A < file # 把文件file中的小写a都变成大写A tr '[:lower:]' '[:upper:]' < file # 把文件file全部大写
tr -d "\r\n" < file
# 终端编码为GB18030编码 $ tr '[:upper:]' '[:lower:]' <<< 琄 琸
为什么琄会变成琸呢?
上面我们说到, tr是按字节来处理的, 而GB18030编码第二个字节编码范围为0×40-0×7E和0×80-0×FE, 这样, 第二个字节就可能出现ASCII码, 我们来看下上面2个汉字的GB18030编码值:
$ od -c <<< 琄 0000000 254 K \n 0000003 $ od -t x1 <<< 琄 0000000 ac 4b 0a 0000003 $ od -c <<< 琸 0000000 254 k \n 0000003 $ od -t x1 <<< 琸 0000000 ac 6b 0a 0000003
看来确实如此, 琄的第二个字节是字符大K, 琸的第二个字节是字符小k.
看来, 如果文本里含有多字节字符, 使用tr的时候得小心咯.
每个编辑器对TAB的显示设置不一样, 有的显示为8个字符, 有的显示为4个字符, 这样就造成了在A编辑器下排版很漂亮, 到了B编辑器下变得一团糟, 所以编码的时候最好使用空白字符代替TAB(Emacs中这样设置: (setq-default indent-tabs-mode nil), ), expand命令也可以帮你把TAB转换成空格
Makefile缩进的时候, 必须是TAB, 所以有时候又需要把空格变成TAB, 就靠unexpand了
colrm [start [stop]]
该命令只支持标准输入, 不支持从文件输入.
删除每行从start到stop之间的字符, 如果stop没有指定的话, 则删除到末尾. 需要注意的是, TAB被认为占8列(不知道为啥这样搞)
切割文本INPUT成文件PREFIXaa, PREFIXab … 默认每个文件1000行, PREFIX为x
split [OPTION]... [INPUT [PREFIX]]
根据模式切割文件, 简单了解即可
csplit [OPTION]... FILE PATTERN...
经常会用到, 主要用来转换编码
我们经常需要知道文件的编码, 这个命令帮你搞定
打扮一下你的文本吧.
按列漂亮的输出:
$ (printf "PERM LINKS OWNER GROUP SIZE DAY HH:MM NAME\n"; ls -l | sed 1d) | column -t
PERM LINKS OWNER GROUP SIZE DAY HH:MM NAME drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-03 22:54 ai drwxr-xr-x 26 taoshanwen taoshanwen 4096 2012-04-15 11:59 algorithm drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-09 13:35 arch drwxr-xr-x 5 taoshanwen taoshanwen 4096 2012-04-03 22:47 c-c++ drwxr-xr-x 6 taoshanwen taoshanwen 4096 2012-04-14 20:33 CIP drwxr-xr-x 5 taoshanwen taoshanwen 4096 2012-04-03 22:47 computer-chess drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-15 00:23 computer-go drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-10 16:25 database drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-15 00:57 distributed drwxr-xr-x 5 taoshanwen taoshanwen 4096 2012-04-03 22:47 genetic-prog drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-03 22:47 infosec drwxr-xr-x 2 taoshanwen taoshanwen 4096 2011-03-19 20:40 iphone drwxr-xr-x 20 taoshanwen taoshanwen 4096 2012-04-15 00:38 java drwxr-xr-x 94 taoshanwen taoshanwen 16384 2012-04-17 20:01 linux drwxr-xr-x 7 taoshanwen taoshanwen 4096 2012-04-10 19:29 math drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-17 15:37 mysql drwxr-xr-x 2 taoshanwen taoshanwen 4096 2011-10-19 17:04 nosql drwxr-xr-x 11 taoshanwen taoshanwen 4096 2012-04-16 12:54 other drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-07 14:03 perl drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-15 00:18 python drwxr-xr-x 6 taoshanwen taoshanwen 4096 2012-04-03 22:50 ruby drwxr-xr-x 52 taoshanwen taoshanwen 4096 2012-04-15 00:59 search-engine drwxr-xr-x 9 taoshanwen taoshanwen 4096 2012-04-15 00:23 software-engineering drwxr-xr-x 5 taoshanwen taoshanwen 4096 2010-10-11 22:56 svnroot drwxr-xr-x 7 taoshanwen taoshanwen 4096 2012-04-14 20:33 web drwxr-xr-x 66 taoshanwen taoshanwen 12288 2012-04-17 23:47 work
将一个比较长的文本行输出进行"折行".
将输入按照指定宽度进行折行, 功能较fold强大些
下面介绍文本处理中两个最强大的命令sed和awk, 它们已经具有一些程序设计语言的特征了, 特别是awk, 所以, 我们的脚本中, 放眼望去, 皆是awk阿. 熟练掌握这两个命令, 你的文本处理功力将会极大的提升阿.
sed是一个流编辑器, 类似ed(行编辑器, 通过各种命令编辑文件), 它提供了各种替换、删除的命令, 使得这些编辑操作能自动化起来.
每次循环开始时, pattern space的内容会被清空, hold space则不会
如果没有指定地址的话, 表示所有行对执行命令. 还可以提供2个地址, 指定一个地址范围, 这2个地址之间以逗号分割, 比如:
ADDRESS1,ADDRESS2
这样, 第一次匹配上ADDRESS1的行与第一次匹配上ADDRESS2的行之间的所有行都会执行对应的命令.
GNU sed还支持下面几种地址范围:
在地址或者地址范围后加感叹号(!), 表示取反.
$ cat ab
ab
ab
ac
ad
ae
ac
ab
$ sed "" ab
ab
ab
ac
ad
ae
ac
ab
ls -l | sed 1d
sed -n 5,10p ab
sed -n "5,10p; 10q" ab
sed -n "p; 18n"
sed -n "8 {p; q}"
a(..(..))
pattern space为abcde, 那么\1为bcde, \2为de
$ cat ab
AB
AB
ac
ad
ae
ac
AB
$ sed -r 's/(AB)/\L\1YYY/' ab
abyyy
abyyy
ac
ad
ae
ac
abyyy
上例中, 本来\1应该是AB, 但是\L把它全变成小写了. 而且后面的YYY也变成小写了.
s命令后的FLAGS可以由下面几种:
sed s/xxx/yyy/p ab
sed -n '/xxx/ {=; p}'
更多的例子参加sed info
sed -n '1!G; $p; h'
sed -r ':a; s/(.*[0-9])([0-9]{3})/\1,\2/; ta' <<< 124523536543652
awk是文本处理的利器, 前面那些命令能干的事它都能搞定.
action要以大括号括起来, 比如:
awk '0{print} 1{print}' .emacs
$ cat test.awk #!/usr/bin/awk -f BEGIN { str = "GET /index.html HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n" print str |& "/inet/tcp/0/www.baidu.com/80" "/inet/tcp/0/www.baidu.com/80" |& getline print } $ awk -f test.awk HTTP/1.1 200 OK
if (a[e] != 2) { ... }
如果输入很大的话, 过会你就会发现你的awk占了很多内存, 原因就是a[e]的时候, 如果awk发现a中没有e, 就会把e插入到a中, 这样一来内存自然越来越大, 正确的判断方法是:
if (!(e in a)) { ... }
用过python的朋友可能会这样写:
if (e not in a) { ... }
很不幸, 没有这样的语法, 而且还不报错, 我猜awk把e not连接成一个字符串了…