- 1 引言
- 2 关于输入
- 3 文本输出
- 3.1 整个输出
- 3.2 部分输出
- 4 文本搜索
- 4.1 grep (Global Regular Expression Print)
- 4.2 fgrep
- 4.3 egrep
- 4.4 rgrep
- 4.5 agrep (approximate grep)
- 4.6 zgrep
- 4.7 sgrep (structured grep)
- 4.8 nrgrep (Nondeterministic Reverse grep)
- 5 文本摘要
- 5.1 wc (word count)
- 5.2 md5sum
- 6 排序去重
- 6.1 sort
- 6.2 tsort (topological sort)
- 6.3 uniq
- 6.4 comm
- 6.5 shuf (shuffle)
- 7 操作字段
- 7.1 cut
- 7.2 paste
- 7.3 join
- 8 操作字符
- 8.1 tr (translate)
- 8.2 expand
- 8.3 unexpand
- 8.4 colrm (COLumn ReMove)
- 9 文本切割
- 9.1 split
- 9.2 csplit (context split)
- 10 文本编码
- 10.1 iconv
- 10.2 enca
- 11 文本格式化
- 11.1 column
- 11.2 fold
- 11.3 fmt
- 12 微语言
- 12.1 sed (Stream EDitor)
- 12.2 awk (Alfred V. Aho, Peter J. Weinberger, Brian W. Kernighan.)
- 13 语言
- 14 实例
- 14.1 我的正则会数学
- 15 参考文献
1 引言
所谓的文本处理是指对文本进行查找、替换、删除、排序等操作, linux在文本处理方面提供了大量优秀的工具, 使得在linux下进行文本处理极其的方便.
我们平常的工作中, 经常会用到文本处理, 比如日志分析, 比如文本抽取, 等等, 所以掌握好文本处理, 将会对我们的工作起到极大的作用.
下面我就来逐个介绍下这些强大的工具, 对于我觉得大家可能比较熟知的工具及用法, 我会略过, 或者粗讲下.
2 关于输入
Linux哲学中, 为了更好的组合各种命令达到更加强大的功能, 大多数文本处理命令的输入既可以是文件, 也可以是标准输入, 如果没有指定输入文件, 则默认从标准输入读数据. 输出都是标准输出, 方面传给管道线的下一个命令, 想要输出到文件的话, 重定向下即可. 下面介绍的这些命令, 如无特殊说明, 则都可以从文件或者标准输入读入数据.
3 文本输出
3.1 整个输出
- echo
输入为命令行参数
非常常用的命令, 主要用作输出字符串. 如果只是为了向管道线的下一个命令传输入的话, 可以使用Here String:
echo xxx | md5sum md5sum <<< xxx
后者速度上应该会快一点, 不需要经过管道.
- -n
不输出换行符(默认输出换行符)
- -e
解释转义符, 常用的转义符 \t, \n; 这个命令还有个最常用的用途就是输出ANSI颜色:echo -e '\033[1; 31mHello, \033[0m\033[1; 33mworld!\033[0m'
Hello, world!
这个工具可以更方便的输出ANSI颜色.
- -E
不解释转义符
- -n
- printf
输入为命令行参数
更强大的输出你想要的文本的命令, 类似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就能搞定了.
- yes [STRING]
输入为命令行参数
不停的输出字符串STRING, 默认是y. 这个命令用处比较少, 但会有用, 比如测试tail命令.
- cat (concatenate)
此命令最大的用途应该就是显示特殊字符了, 如果你告诉我你经常用它给管道线的下一个命令传输入的话, 那么就太浪费了:cat file | grep xxx grep xxx file
前者相比后者多启动了一个进程, 还经过了管道. file很大的话, 性能差距很容易就看出来了.
- -E
在行尾显示$, 有时候行尾是空格或者TAB, 看不清行尾是哪里, 加了这个选项就知道了
- -T
输出的时候用^I代替TAB符号, 这样很容易知道那一坨空白到底谁是TAB了
- -v
用^和M-表示法输出不可打印字符taoshanwen@taoshanwen-laptop ~$ echo -e '\r' | cat -v ^M
- -e
相当于-vE
- -t
相当于-vT
- -A
相当于-vET, 一般这个选项用的最多
- -n
显示行号, 应该有同学感兴趣
- -b
只对非空行显示行号, 有人感兴趣吗?
- -s
对连续的空行只输出一个空行, 见过好多同学有这样的需求
- -E
- tac
看名字知道了, 它和cat是反的, 倒着输出, 先输出最后一行, 接着是倒数第二行, 最后输出的是第一行- -s, –separator=STRING
设定分隔符, 代替\n分割文本
- -s, –separator=STRING
- rev (reverse)
这个命令和cat也是反的, 不过它不像tac那样, 它输出行的顺序和cat也是一样的, 不过输出每行的时候, 先输出最后一个字符, 接着是倒数第二个字符, 最后才是第一个字符, 下面这个命令可以把输入全部倒过来:tac | rev
- nl (number lines)
更强大的行号显示工具, 可以控制行号的格式, 宽度. 没有特殊的需求, 用cat -n就够了.
- head
只显示文本的开头几行, 比如head -2只显示前面2行
- tail
这个命令相对head来说, 最常用的用途就是不停的打印文件的最新内容了(tail -f)- -n, -K, –lines=K
显示尾部K行, -n +K显示第K行到文本尾部的所有内容
- -f, –follow[={name|descriptor}]
这个选项表示如果文件尾部有新数据追加进来, 也会显示出来. 这个选项可以根据文件名(name)和文件描述符(descriptor)来监视文件是否有更新. 默认是descriptor.
- –retry
当文件不可访问时, 进行重试, 这个选项和–follow=name组合起来比较有用
- -F
相当于–follow=name –retry
- -n, -K, –lines=K
4.1 grep (Global Regular Expression Print)非常常用的命令, 打印文本中匹配模式的行, 下面的选项最好都能掌握.
grep [OPTIONS] PATTERN [FILE...] grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
- -E, –extended-regexp
使用扩展正则表达式(ERE), 默认的是基础正则表达式(BRE), BRE中元字符?, +, {, |, (, )失去特殊意义(你是否遇到grep "a|b"是否不能打印出含有a或者b的行?), 想要表达特殊意义的话, 需要用转义字符进行转义(\), \?, \+等
- -F, –fixed-strings
把pattern当作一个固定的字符串, 不进行正则解析. 当你要搜索非正则的固定字符串时(还可能含有正则元字符), 这个选项会非常有用, 而且由于不需要解析正则, 速度会快些.
- -P, –perl-regexp
把pattern解析为perl的正则(PCRE), 由于perl的正则强大而简洁, 所以可以多尝试着使用这个选项.
- -i, –ignore-case
搜索pattern的时候, 忽略大小写. 如果没有这个选项, 可以使用PCRE, 比如:grep -P "(?i)AB"
- -v, –invert-match
显示不能匹配pattern的行
- -e pattern
如果你想要指定多个搜索pattern, 或者你想要搜索的pattern由减号(-)开头(如果直接grep pattern的话, 会被解析为grep的选项), 就可以用这个选项了.
- -f FILE, –file=FILE
从文件中获取pattern, 每行一个pattern
- -x, –line-regexp
pattern必须要匹配整行, 这个选项等价于 "^pattern$"
- -w, –word-regexp
pattern必须要匹配整个单词, 这个选项等价于 "\bpattern\b"
- -c, –count
不打印匹配的行, 只打印匹配的行数, 等价于grep pattern | wc -l
- –color[=WHEN], –colour[=WHEN]
用颜色高亮出匹配的:(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))
- -m NUM, –max-count=NUM
当发现NUM个匹配行后, 停止扫描剩下的文本.
- -q, –quiet, –silent
不打印任何信息, 发现匹配即退出, 并返回0, 否则返回1. 我们经常只是想查看整个文本里面是否有匹配, 这时候这个选项就非常有用了, 速度会快很多.
- -o, –only-matching
只显示匹配pattern的字符串, 匹配行的其余部分不显示
- -n, –line-number
在匹配行前面打印行号 有时候想看看匹配行周围都是啥, 下面这几个选项就非常有用了:
- -A NUM, –after-context=NUM
打印匹配行的后面NUM行
- -B NUM, –before-context=NUM
打印匹配行的前面NUM行
- -C NUM, -NUM, –context=NUM
打印匹配行的周围NUM行
- -a, –text
有时候文件中含有一些非可打印字符, grep可能会把它识别成二进制文件, 这时候grep只会打印出是否匹配pattern的信息, 并不会打印匹配的每行, 这个选项会强制grep把该文件当文本文件处理
- -R, -r, –recursive
递归处理文件夹下的所有文件
- -l, –files-with-matches
不打印匹配的行, 只打印匹配的文件
- -L, –files-without-match
和-l相反, 不打印匹配的行, 只打印不匹配的文件
- -h, –no-filename
搜索多个文件时, 会在每行前面输出文件名, 如果你不喜欢, 使用此选项吧.
4.3 egrepgrep -E
4.4 rgrepgrep -r
4.5 agrep (approximate grep)grep的模糊匹配版本
4.6 zgrep对压缩文件进行grep, 接受的选项和grep完全一样
4.7 sgrep (structured grep)对结构化的文本, 如SGML、XML、HTML进行搜索、抽取, 功能非常强大
4.8 nrgrep (Nondeterministic Reverse grep)类似agrep
5 文本摘要
5.1 wc (word count)最主要的用途就是统计行数
- -l, –lines
最常用的选项, 统计行数
- -L, –max-line-length
输出文本最长行的长度
- -w, –words
输出单词数
- -m, –chars
输出字符数
- -c, –bytes
输出字节数
6 排序去重
6.1 sort非常常用的命令, 啥序都能排
- -r, –reverse
逆序排序, 默认是按从小到大排, -r后就从大到小了
- -c, –check, –check=diagnose-first
检查输入文件是否是有序的, 不是的话, 会打印哪行开始不是有序的
- -C, –check=quiet, –check=silent
类似-c, 但是不打印错误信息, 只返回错误码1
- -k, –key=POS1[,POS2]
这个应该是sort最nb的地方了, 可以精确控制要排序的对象. POS具备这样的形式: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 EOFaa 224 bb 113 cc 513 cc 323 dd 444
- -u, –unique
对输出结果进行去重, 只输出重复的记录中的第一条记录
- -m, –merge
对有序的输入文件进行归并, 这个选项使得你能够在多核机器上优化大数据集的排序
- -s, –stable
使得sort成为稳定排序
- -T, –temporary-directory=DIR
设定指定的临时文件夹, 存放中间数据. 当你排序非常大的文件时, 而且/tmp所在的分区空间不够时, 就会用到该选项了
- -n, –numeric-sort
把输入当整数来排序, 可以有负数, 但是不能含有加号(+)的正数, 这种输入用-g搞定吧
- -g, –general-numeric-sort
把输入当作数值来排序, 可以有浮点数. 如果输入是整数的话, 就用-n搞定吧, 人家性能高些.
- -h, –human-numeric-sort
可以排序2K, 1G等带单位的数字, 很爽啊, 想排序某文件夹下所有文件和文件的大小吗:du -sh * | sort -h
- -M, –month-sort
按月份进行排序, `JAN' < `FEB' < … < `DEC'
- -d, –dictionary-order
按字典序排序, 忽略字母、数字、空白字符外的所有字符
- -V, –version-sort
你开发的软件有很多版了没? 排下吧, 根据版本号
- -t, –field-separator=SEP
设置字段分隔符, 默认为空白字符. 可惜的是, 这个字段分隔符只能为单个字符
- -b, –ignore-leading-blanks
忽略前导空白字符
- -f, –ignore-case
忽略大小写
- -i, –ignore-nonprinting
忽略不可打印字符
- -R, –random-sort
随机排序, 我想你会用到它的, 反正我用过几次. 不过排序结果不完全随机, 因为sort会先对每行进行hash, 然后对hash值进行排序, 所以相同的行一定会排到一块. 不过也许, 这正是你想要的. 如果你想更乱或者更加强大的功能的话, 看这里
- 陷阱
你是否经常sort一个中文文件却得不到正确结果? 那就对了, 你肯定没设置好语言环境(locale), 试试LC_ALL=C sort吧. sort会根据本地语言环境对输入文本进行排序. LC_ALL=C表示会根据字节值来排序. 或许你说我怎么见到的都是LANG=C sort啊, 来, 我们看看bash info上关于LANG和LC_ALL的解释:LANGUsed 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, anddeterminesthe behaviorofrangeexpressions,equivalenceclasses, and collating sequences within pathname expansion and pattern matching.
- LANG
如果你没有用LC_来设定某个分类的locale, 将会使用LANG来决定这个分类的locale - LC_ALL
该变量会覆盖LANG和LC_ - LC_COLLATE
该变量设置排序时的locale
- LANG
tsort <
输出:
a b c d e f
6.3 uniq也是非常常用的一个命令. 这个命令主要用来对有序序列进行去重, 所以它常和sort联合起来使用, 但是sort -u本身就有去重的功能, 所以当你仅仅只是为了去重时, sort -u就可以帮你搞定了(当输入文本巨大时, 可以用hash来去重提高性能, 比如awk的关联数组), 所以呢, 当年需要对重复的数据进行统计时, 会用到uniq. 当然其实uniq相比sort -u而言, 对重复数据有更加强大的处理
- -c, –count
在每行文本前面输出重复次数
- -d, –repeated
只显示重复的行, 重复的行只显示一行
- -D, –all-repeated[=delimit-method]
显示所有重复的行, 注意该选项与选项-d的区别
- -u, –unique
只打印不重复的行
- -i, –ignore-case
比较的时候不区分大小写
- -f, –skip-fields=N
不比较前面N个字段, 字段分隔符为空白字符
- -s, –skip-chars=N
不比较前面N个字符
- -w, –check-chars=N
每行最多比较前面N个字符
- 实例演示
大家看了uniq上面几个选项后, 是不是有uniq没有太大用处的感觉? 这都是错觉, 下面我给大家演示下uniq在集合运算(统计中有大量的应用)方面巧妙的应用.
- 并集
sort A B | uniq
- 交集
sort A B | uniq -d
- 差集(A-B)
sort A B B | uniq -u6.4 comm逐行比较两个有序文件, 分三列输出文件1独有的行、文件2独有的行、文件12共有的行,
- 缺陷
- 不能控制字段分隔符
- 不能像sort -k那样精确的控制要比较的对象
$ cat ab ax by cz$ cat ac ax bd cz$ comm ab ac ax bd by cz
- -1
不输出第一列(文件1独有的行)
- -2
不输出第二列(文件2独有的行)
- -3
不输出第三列文件3独有的行
- –check-order
检查输入文件是否有序
- –nocheck-order
不检查输入文件是否有序
- –output-delimiter=STR
设定输出分隔符, 默认为TAB6.5 shuf (shuffle)如果sort -R产生的结果还不够乱的话, 我想这个命令应该就是你需要的了. 该命令产生完全乱序的结果, 而且速度应该比sort -R快(shuf不用排序), 还有功能更强大
- 实例演示
- 交集
comm -12 <(sort A) <(sort B)
- 差集(A-B)
comm -23 <(sort A) <(sort B)
- -e, –echo
对命令行参数乱序7 操作字段
- -i, –input-range=LO-HI
对LO到HI之间的数字进行乱序, 比如shuf -i 12-100
7.1 cut挺常用的一个命令, 能非常方便的取某个字段
- -f, –fields=LIST
选择要输出的字段
- -c, –characters=LIST
选择要输出的字符
- -b, –bytes=LIST
选择要输出的字节
- -d, –delimiter=DELIM
设定字段分隔符, 默认是TAB. 可惜的是, 该分隔符也只能是单个字符.
- –complement
取设定的选择LIST的补集
- -s, –only-delimited
忽略不包含分隔符的行, 默认操作是输出整行
- –output-delimiter=STRING
设定输出分隔符7.2 paste这个命令很有意思, 把两个文件按行粘贴到一块, 曾经我想自己写个程序搞定这个需求, 后来发现linux下竟然已经有这玩意了(linux总能给你带来惊喜)
- LIST
-f, -c, -b选项使用的列表, 可以有下面几种形式:列表可以有多个, 之间以逗号分割, 比如:
- N
第N个字段/字节/字符- N-
从第N个到最后一个- M-N
从第M个到第N个- -M
从第一个到第N个
cut -f1-3,4-7
$ cat num2 1 2 $ cat let3 a b c $ paste num2 let3 1a 2b c
- -d, –delimiters=LIST
paste两个文件的时候, 默认是用TAB分割, 这个选项设定分隔符, 同为可惜的是, 只能为单个字符(主要是paste可以粘贴多个文件, 这个选项的第二个字符用来分割第二个和第三个文件)7.3 join这是一个稍微高级点的命令, 它把输入文件当成一个key/value对, 然后会把同一个key的所有value粘贴到一块, 来个例子:
- -s, –serial
默认paste是竖着粘贴的, 加了这个选项后, 就横着粘贴了:$ paste -s num2 let3 12 abc
$ 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的字段必须有序.
- -i, –ignore-case
比较字段时, 忽略大小写
- -t CHAR
使用CHAR作为字段分隔符, 又是只能为单个字符(杯具…)
- -1 FIELD
设定第一个文件的key为第FIELD个字段
- -2 FIELD
设定第二个文件的key为第FIELD个字段
- -j FIELD
-1 FIELD -2 FIELD
- -a FILENUM
join默认只打印拥有相同key的行, 该选项会打印第FILENUM个文件中没有匹配上的行
- -v FILENUM
和-a选项有点类似, 该选项只打印第FILENUM个文件中没有匹配上的行, 不会打印匹配上的行
- –check-order
检查输入文件作为key的字段是否有序
- –nocheck-order
不检查输入文件作为key的字段是否有序
- -o FIELD-LIST
高级的控制输出对象的选项, FIELD-LIST中的每个元素具有下面这样的形式:每个元素之间以逗号或者空格分割
- 0
表示做为key的字段- M.N
M为文件号, 取值为0或者1, N为字段号, M.N就是取第M个文件第N个字段
8 操作字符
- -e EMPTY
-o选项中, 可能文件M中没有字段号N, 这时候输出的时候用EMPTY代替.
8.1 tr (translate)主要对文本中的字符进行替换、删除.
- 该命令只支持标准输入, 不支持从文件输入.
- tr仅支持单字节字符.
tr [OPTION]... SET1 [SET2]
字符集合可以由一系列的字符构成, 也可以具有以下形式:
当提供2个字符集合时, 表示把SET1中的字符替换成SET2中的对应的字符, 比如:
- CHAR1-CHAR2
从CHAR1到CHAR2的所有字符- [CHAR*]
这种形式只能出现在SET2中, 表示拷贝CHAR直到SET2和SET1的长度相等- [CHAR*REPEAT]
REPEAT个CHAR- [:alnum:]
所有的字母和数字- [:alpha:]
所有的字母- [=CHAR=]
和CHAR属于同一个字符类中的所有字符
tr a A < file # 把文件file中的小写a都变成大写A tr '[:lower:]' '[:upper:]' < file # 把文件file全部大写
- -d, –delete
删除出现在集合1中的所有字符. 下面的命令把文件file中所有的行连成一行:tr -d "\r\n" < file
- -s, –squeeze-repeats
把SET1中连续的字符都替换成1个字符, 当SET2也提供时, 首先执行删除连续字符操作, 然后才执行替换操作
- -c, -C, –complement
使用SET1的补集
- -t, –truncate-set1
首先把SET1中的字符截断到和SET2长度相等8.2 expand每个编辑器对TAB的显示设置不一样, 有的显示为8个字符, 有的显示为4个字符, 这样就造成了在A编辑器下排版很漂亮, 到了B编辑器下变得一团糟, 所以编码的时候最好使用空白字符代替TAB(Emacs中这样设置: (setq-default indent-tabs-mode nil),), expand命令也可以帮你把TAB转换成空格
- 陷阱
经常见到有同学对会含有中文的文件用上面的方法进行大小写转换:# 终端编码为GB18030编码 $ tr '[:upper:]' '[:lower:]' <<< 琄 琸
为什么琄会变成琸呢?
上面我们说到, tr是按字节来处理的, 而GB18030编码第二个字节编码范围为0×40-0×7E和0×80-0×FE, 这样, 第二个字节就可能出现ASCII码, 我们来看下上面2个汉字的GB18030编码值:
$ od -c<<< 琄 0000000 254K\n 0000003 $ od -t x1<<< 琄 0000000 ac 4b 0a 0000003$ od -c<<< 琸 0000000 254k\n 0000003 $ od -t x1<<< 琸 0000000 ac 6b 0a 0000003
看来确实如此, 琄的第二个字节是字符大K, 琸的第二个字节是字符小k.
看来, 如果文本里含有多字节字符, 使用tr的时候得小心咯.
- -i, –initial
不转换非空白字符后的TAB8.3 unexpandMakefile缩进的时候, 必须是TAB, 所以有时候又需要把空格变成TAB, 就靠unexpand了
- -t, –tabs=NUMBER
设置一个TAB转换成几个空格, 默认是8
- -a, –all
转换所有的空格, 默认情况下只转换开头的空格
- –first-only
只转换开头的空格8.4 colrm (COLumn ReMove)
- -t, –tabs=N
几个空格转换成一个TAB, 默认是8colrm [start [stop]]
该命令只支持标准输入, 不支持从文件输入.
删除每行从start到stop之间的字符, 如果stop没有指定的话, 则删除到末尾. 需要注意的是, TAB被认为占8列(不知道为啥这样搞)
9 文本切割
9.1 split切割文本INPUT成文件PREFIXaa, PREFIXab … 默认每个文件1000行, PREFIX为x
split [OPTION]... [INPUT [PREFIX]]
- -l, –lines=NUMBER
按行切割, 每个输出文件NUMBER行, 比较常用的切割方式
- -b, –bytes=SIZE
按字节切分
- -C, –line-bytes=SIZE
每个文件最多SIZE个自己, 但是每行都完整的保存到一个输出文件中, 即不像-b那样, 可能一个整行被拆分到多个文件中去9.2 csplit (context split)根据模式切割文件, 简单了解即可
- -d, –numeric-suffixes
使用数字做为后缀名
csplit [OPTION]... FILE PATTERN...
10 文本编码
10.1 iconv经常会用到, 主要用来转换编码
- –list, -l
列出可以识别的编码10.2 enca我们经常需要知道文件的编码, 这个命令帮你搞定
- -c
转换的时候, 忽视无效的字符, 如果没有加这个选项, iconv碰到这个无效字符会直接报错退出, 所以最好加上这个选项
11 文本格式化
打扮一下你的文本吧.
11.1 column按列漂亮的输出:
$ (printf "PERM LINKS OWNER GROUP SIZE DAY HH:MM NAME\n"; ls -l | sed 1d) | column -t
PERMLINKSOWNERGROUPSIZEDAYHH:MMNAME drwxr-xr-x3taoshanwentaoshanwen40962012-04-0322:54ai drwxr-xr-x26taoshanwentaoshanwen40962012-04-1511:59algorithm drwxr-xr-x2taoshanwentaoshanwen40962012-04-0913:35arch drwxr-xr-x5taoshanwentaoshanwen40962012-04-0322:47c-c++ drwxr-xr-x6taoshanwentaoshanwen40962012-04-1420:33CIP drwxr-xr-x5taoshanwentaoshanwen40962012-04-0322:47computer-chess drwxr-xr-x2taoshanwentaoshanwen40962012-04-1500:23computer-go drwxr-xr-x3taoshanwentaoshanwen40962012-04-1016:25database drwxr-xr-x3taoshanwentaoshanwen40962012-04-1500:57distributed drwxr-xr-x5taoshanwentaoshanwen40962012-04-0322:47genetic-prog drwxr-xr-x3taoshanwentaoshanwen40962012-04-0322:47infosec drwxr-xr-x2taoshanwentaoshanwen40962011-03-1920:40iphone drwxr-xr-x20taoshanwentaoshanwen40962012-04-1500:38java drwxr-xr-x94taoshanwentaoshanwen163842012-04-1720:01linux drwxr-xr-x7taoshanwentaoshanwen40962012-04-1019:29math drwxr-xr-x2taoshanwentaoshanwen40962012-04-1715:37mysql drwxr-xr-x2taoshanwentaoshanwen40962011-10-1917:04nosql drwxr-xr-x11taoshanwentaoshanwen40962012-04-1612:54other drwxr-xr-x2taoshanwentaoshanwen40962012-04-0714:03perl drwxr-xr-x3taoshanwentaoshanwen40962012-04-1500:18python drwxr-xr-x6taoshanwentaoshanwen40962012-04-0322:50ruby drwxr-xr-x52taoshanwentaoshanwen40962012-04-1500:59search-engine drwxr-xr-x9taoshanwentaoshanwen40962012-04-1500:23software-engineering drwxr-xr-x5taoshanwentaoshanwen40962010-10-1122:56svnroot drwxr-xr-x7taoshanwentaoshanwen40962012-04-1420:33web drwxr-xr-x66taoshanwentaoshanwen122882012-04-1723:47work
11.2 fold将一个比较长的文本行输出进行"折行".
11.3 fmt将输入按照指定宽度进行折行, 功能较fold强大些
12 微语言
下面介绍文本处理中两个最强大的命令sed和awk, 它们已经具有一些程序设计语言的特征了, 特别是awk, 所以, 我们的脚本中, 放眼望去, 皆是awk阿. 熟练掌握这两个命令, 你的文本处理功力将会极大的提升阿.
12.1 sed (Stream EDitor)sed是一个流编辑器, 类似ed(行编辑器, 通过各种命令编辑文件), 它提供了各种替换、删除的命令, 使得这些编辑操作能自动化起来.
- 工作流程
sed维护2快内存(也可以理解为2个变量, 或者说是2个寄存器), 分别叫做 pattern space 和 hold space, sed对每行输入执行下面的循环:每次循环开始时, pattern space的内容会被清空, hold space则不会
- 读入输入行, 去掉尾部的换行符, 存入pattern space
- 执行sed命令, 每条sed命令都可以有个地址与它关联, 这个地址就类似于条件语句, 只有这个条件语句通过验证时, 其对应的命令才会执行
- 执行完所有的sed命令后, 如果没有指定sed的-n选项, 将会打印pattern space的内容, 然后再输出换行符. 最后继续读入下一行, 进行下一次的循环
- 地址格式
sed地址可以具有以下的形式:如果没有指定地址的话, 表示所有行对执行命令. 还可以提供2个地址, 指定一个地址范围, 这2个地址之间以逗号分割, 比如:
- NUMBER
指定执行命令的行号, 只有在这行, 对应的命令才会被执行, 行号从1开始, 另外, 如果没有指定-i或者-s选项的话, 所有的输入文件会被当成一个输入流, 行号就会一直累加的- FIRST~STEP
在FIRST、FIRST+STEP、FIRST+2*STEP、、FIRST+3*STEP行执行对应的命令- $
最后一行- REGEXP
在匹配上正则REGEXP的行执行对应的命令, 如果REGEXP中含有/, 需要用\转义- \%REGEXP%
在匹配上正则REGEXP的行执行对应的命令, %也可以是其他字符, 如果REGEXP中含有%, 需要用\转义- /REGEXP/I, \%REGEXP%I
忽略大小写- /REGEXP/M, \%REGEXP%M
可以匹配多行, M表示multi-line
ADDRESS1,ADDRESS2
这样, 第一次匹配上ADDRESS1的行与第一次匹配上ADDRESS2的行之间的所有行都会执行对应的命令.
GNU sed还支持下面几种地址范围:
在地址或者地址范围后加感叹号(!), 表示取反.
- ADDR1,+N
匹配ADDR1, 以及接下来的N行- ADDR1,~N
匹配ADDR1, 直到行号为N倍数的行
- 常用命令
由于sed默认会打印pattern space, 所以不加任何命令的话, 就和cat一样, 打印所有的输出:$ cat ab ab ab ac ad ae ac ab$ sed "" ab ab ab ac ad ae ac ab
- d
删除pattern space, 立即进入下一轮循环.
ls输出的时候, 第一行有个摘要, 如果不想显示的话, 这样:ls -l | sed 1d
- p
打印pattern space.
输出文件ab第5到第10行:sed -n 5,10p ab
- q [EXIT-CODE]
立即以返回码EXIT-CODE(默认为0)退出sed, 如果没有加-n选项的话, 当前的模式空间也会打印出来.
如果文件很大的话, 下面的方法输出文件ab第5到第10行会快很多:sed -n "5,10p; 10q" ab
- n
如果没有加-n的话, 打印模式空间, 然后直接进入下一轮循环.
不打印第18行:sed -n "p; 18n"
- { COMMANDS }
一组命令, 这组命令共用同一个地址.
打印第8行:sed -n "8 {p; q}"
- s/REGEXP/REPLACEMENT/FLAGS
这个命令估计是大家用的最多的命令了. 前面几个命令大家不知道的情况下, 可能通过其他命令解决了, 但是这个命令的功能除了awk, 其他的做不了, 而sed比awk更简洁.
这个命令主要是对pattern space进行替换, 对匹配REGEXP的部分用REPLACEMENT进行替换, 用来分割的/可以由其他字符组成, 比如s:REGEXP:REPLACEMENT:FLAGS. REPLACEMENT可以由原始的字符组成, 也可以由下面带有特殊意义的串组成:s命令后的FLAGS可以由下面几种:
- &
匹配REGEXP的部分- \d
d为1-9的数字, \d表示REGEXP中第d个括号匹配的部分, 比如REGEXP为:a(..(..))
pattern space为abcde, 那么\1为bcde, \2为de
- \L
把REPLACEMENT中的字符变成小写, 直到遇到\U和\E. 比如:$ 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也变成小写了.
- \l
把下一个字符变成小写- \U
把REPLACEMENT中的字符变成大写, 直到遇到\L和\E.- \u
把下一个字符变成大写- \E
结束\L和\U的作用
- g
s命令默认只替换第一个匹配, g可以让它全部替换- NUMBER
替换第NUMBER个匹配- p
如果发生了替换, 打印模式空间.
搜索文件ab中xxx并替换成yyy打印出来:sed s/xxx/yyy/p ab
- i, I
正则匹配忽略大小写
- y/SOURCE-CHARS/DEST-CHARS/
类似tr命令, 用DEST-CHARS对应的字符替换出现在SOURCE-CHARS中的字符. 和s命令一样, 分隔符/也可以是其他字符.
- =
打印行号. 下面的命令类似grep -n:sed-n '/xxx/ {=; p}'
- 高级命令
- h
用pattern space替换hold space
- H
先在hold space追加换行符, 再往hold space追加pattern space
- g
用hold space替换pattern space
- G
先在pattern space追加换行符, 再往pattern space追加hold space
- x
交换pattern space和hold space的内容
- D
删除模式空间的第一行. 如果模式空间中还有内容的话, 开始进入下一轮循环, 但不读入输入. 如果没有内容的话, 读入输入并进行下一轮循环.
- N
追加换行符到pattern space, 并读入下一行输入追加到pattern space, 如果已经没有任何输入, 直接退出sed, 不再处理任何命令.
- P
大写p, 打印pattern space第一行
- z
清空pattern space
- 专家命令
- : LABEL
设定标签, 类似C语言中设定一个标签, 然后可以goto之
- b [LABEL]
跳转到标签, 如果没有提供标签的话, 直接进入下一轮循环
- t [LABEL]
这轮循环中, 如果s命令替换成功过, 则跳转到标签. 如果没有提供标签的话, 直接进入下一轮循环
- T LABEL
和t LABEL相反, s命令替换失败, 才跳转到标签
- 命令选项
- -n, –quiet, –silent
禁止自动打印pattern space
- -i[SUFFIX], –in-place[=SUFFIX]
原地编辑文件, 文本修改后会直接影响到输入文件
- -r, –regexp-extended
启用扩展正则, 默认是基础正则
- -s, –separate
把每个文件当作单独的输入流, 而不是一个输入流12.2 awk (Alfred V. Aho, Peter J. Weinberger, Brian W. Kernighan.)awk是文本处理的利器, 前面那些命令能干的事它都能搞定.
- 给我一个寄存器, 我可以干好多事
各位看完上面所说的sed命令后, 是不是觉得sed只能做一些替换、删除等操作, 为什么称为微语言呢, 我之所以把它归到微语言这一类是有原因的, 它具有下面几个语言的特征:更多的例子参加sed info
- 有条件判断能力, sed的地址就是一种条件判断, 还有标签命令也是条件判断
- 有流程控制能力, 标签命令就可以做到
- 有变量, 虽然很少, 只有2个, 但是仍然可以干好多事了, 看下面的例子
- tac
模拟tacsed -n '1!G; $p; h'
- 为数字字串增加逗号分隔符号, 将1234567变为1,234,567
sed -r ':a; s/(.*[0-9])([0-9]{3})/\1,\2/; ta' <<< 124523536543652
- 工作流程
awk的工作方式有点类似sed, sed是地址+命令, awk则是pattern+action, pattern是要匹配的模式, action是要执行的命令, pattern可以由下面几种形式:action要以大括号括起来, 比如:
- BEGIN
awk程序开始处理输入时- END
awk程序结束处理输入时- BEGINFILE
awk程序开始处理每个文件时- ENDFILE
awk程序结束处理每个文件时- regular expression
- relational expression
关系表达式- pattern && pattern
- pattern || pattern
- pattern ? pattern : pattern
- (pattern)
- ! pattern
- pattern1, pattern2
awk '0{print} 1{print}' .emacs
- 内置变量
- ARGC
awk输入参数的个数, 不包括awk自己- ARGV
命令行参数- ARGIND
当前处理的文件在ARGV中的位置, ARGV[ARGIND]表示当前正在处理的文件, 可以通过这个变量来对不同的输入文件做不同的处理- FNR (File Number Record)
当前文件的记录总数- NR (Number Record)
目前处理的记录总数- NF (Number of Field)
当前记录的字段数
- 网络编程
awk能开发网络程序, 你相信吗?$ cat test.awk #!/usr/bin/awk -fBEGIN { 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
13 语言
- 陷阱
- tolower/toupper
和tr一样, 这2个函数也是对字节进行处理
- 判断元素是否存在
你是否这样判断某元素是否存在于某数组: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连接成一个字符串了…
14 实例
- a2p
- s2p
- perl
- python
14.1 我的正则会数学15 参考文献
- multi-sort
【Linux下文本的高效处理】转自:http://ahei.info/text-processing.htm
- 相关命令的info及coreutils的info
- 高级Bash脚本编程指南: 文本处理命令
- Survey of Global Regular Expression Print (GREP) Tools
推荐阅读
- Linux|109 个实用 shell 脚本
- linux笔记|linux 常用命令汇总(面向面试)
- Linux|Linux--网络基础
- linux|apt update和apt upgrade命令 - 有什么区别()
- linux|2022年云原生趋势
- Go|Docker后端部署详解(Go+Nginx)
- 开源生态|GPL、MIT、Apache...开发者如何选择开源协议(一文讲清根本区别)
- GitHub|7 款可替代 top 命令的工具