看例子学sed
这次重温一下Linux/Unix下一个很老(反正比我老)很有用的流编辑器:sed(stream editor)。要是不经常使用,很容易忘记。可以把本文当成一个例子库,有用的时候来查一下。后来还写了篇看例子学awk。
假设我们有一个csv文件如下:
增
在文件头/尾增加一行
|
|
如果没有-i
,第一行命令会输出新的文件内容但不会改变staff.csv
。1i
中的1
是指第1行,i
是指在读取文件此行前增加(include)记录。如果把i
换成a
,指的是读取文件此行后增加(append)记录。是不是有点vi的感觉?下面这条命令的结果就会把新行插入到第三行:
要是想在文件尾增加一行的话,用:
其中的$
指最后一行。如果要在倒数第二行增加一行呢?把a
换成i
吧。倒数第三行呢?你确认你真的有这么奇葩的需求么…
在匹配的地方增加一行
如果我们要在Jane上面增加一行,这么做:
意思是当匹配到Jane
的时候,便做后面的操作。接下来的i
不用说了吧,也能替换成a
。这里的匹配指的是部分匹配,也能匹配多行。如果需要插入的行以空格开头,就用反斜杠\
来转义这个空格。试试下列命令:
下面这个命令可以在Gavo后面增加两行:
删
删除特定行
删(delete)和増很相似,区别是把i
或a
换成d
即可:
删除关联行
下面这个命令把Gavo这一行和下一行都删掉:
其中的N
就是下一行(next line)的意思。如果不想删除Gavo这行,用这个命令:
相当于匹配了两行也就是Gavo,35\nJane,21
之后,再把\n
之后的所有文本替换成空白,即删除。替换的命令在下面的改中会详细介绍。
改
所有行头/尾增加项目
|
|
其中的s
表示替换(substitute),^
表示开头,相对应的$
表示结尾。
所有行修改项目
|
|
这样就能把所有的China换成US。如果想把所有的名字后面都加上一个-dev
呢?运行:
其中第一条命令的正则表达式,[A-Z][a-z]*
匹配逗号和名字,&
表示匹配上的内容,比如对于第二行来说,是,Gavo
。第二条命令略微麻烦点,-r
表示扩展的正则表达式(extended regular expressions),圆括号表示分组,第一个圆括号中间是第一组,替换的时候用\1
表示匹配上的内容。所以\1
就是,
之前的文本。文件的每一行都有两个逗号,sed会匹配最远的那一个。比如对于第二行来说,匹配到了第二个逗号,所以\1
的值就是US,Gavo
。加完-dev
之后要再补上逗号。所以sed是非常灵活的,可以用多种办法来实现一个功能。
正则替换
给所有的项目都加上引号:
上面的命令是实现的两种方式。第一条命令的意思是除了逗号以外的所有匹配文本都加双引号。对于第二行来说,匹配到了三个文本:US
,Gavo
和35
。第二条命令的思路则完全不同,是先在首尾都加上双引号,然后再把所有的,
都替换成","
。中间的竖线|
用反斜杠转义后就是正则表达式中的“或”的意思。如果不想用正则的方式,只想做完全匹配的字符串替换,可以用perl
,例如:
全局替换
把所有的l
改成L
:
后面的/g
代表整行范围内的所有匹配全部替换,不加g
的话就会被替换成BiLl
。可以换成2
只替换第二个匹配项。还可以选择i
来忽略大小写,也可以一起用。它们都是正则表达式的范畴。
同时替换l
和m
,以下两种方式都可以:
大小写替换
|
|
\L
就是全部小写(lowercase),\U
就是全部大写(uppercase)。&
在上文有提到,表示匹配上的内容。
修改指定行
现在表头的第一列也成了US,把它改成Country:
把第2行到4行的US替换成China:
把第3行整行替换掉:
删除所有符号
|
|
[[:punct:]]
是正则表达式中预先定义的子字符类(character classes),代表所有的标点符号。sed支持的子字符类如下:
- [:alnum:]:[0-9A-Za-z]
- [:alpha:]:[A-Za-z]
- [:blank:]:空格和TAB
- [:cntrl:]:控制字符(Control characters),ASCII码为000~037和177 (DEL)
- [:digit:]:[0-9]
- [:graph:]:[:alnum:]和[:punct:]
- [:lower:]:[a-z]
- [:print:]:[:alnum:]、[:punct:]和空格
- [:punct:]:符号 ! “ # $ % & ‘ ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
- [:space:]:[:blank:]和回车、换行等
- [:upper:]:[A-Z]
- [:xdigit:]:16进制 [0-9A-Fa-f]
使用变量
如果想用变量里的值来代替,可以这么写(注意是双引号而不是单引号了):
查
查看特定行
查(print)也和増很类似,区别是把i
或a
换成p
即可:
但是…只是把匹配的行多打一遍而已。如果想要达到grep
般的效果,加上-n
就可以了:
倒数第二个命令中的21$
表示以21结尾。如果要以21开头,用^21
。最后一个命令中的!
是取反的意思,所以21的记录就反而被隐藏了,而其他的记录倒都显示出来了。
查看行范围
如果想要查看直到匹配某条记录,用下面这条命令:
其中的q
代表查到后退出(quit)。还有几种方式:
查看奇/偶数行
|
|
第一条命令中的n;
表示输出当前行并立即读取下一行。第二条命令先把第一行记录删除,于是再输出的奇数行就自然变成原来的偶数行了。
从单行中查找
比如想从I am 18 years old里查找18这个年龄,可以这么做:
这里\1
代表第一个被匹配上的内容也就是\(.*\)
。发挥想象力:
参考资料
The UNIX School 里的awk and sed tutorials含有大量的例子和解释,非常容易上手,本文就是以其为基础整理而成。
酷壳的sed 简明教程很适合入门。
当然还有最全面的官方文档。