Linux文件处理三剑客之sed
背景
sed命令最初是由李·麦克马洪(Lee E. McMahon)在1973年所创造。sed(Stream Editor)是一个流编辑器,用于在Unix操作系统中对文本进行编辑。它允许用户对输入的文本进行各种操作,如替换、删除、插入和其他编辑操作,这些操作可以通过命令行进行控制。sed命令的创造为Unix系统用户提供了一种强大的文本处理工具,使得对文本进行批量处理和编辑变得更加高效和方便。
当处理文本或者多行输入时,sed命令将一行行进行筛选处理,而不会直接跳跃到匹配行。
格式
sed [OPTION]... {script-only-if-no-other-script} [input-file]...
其中 :
-
OPTION为选项
-
script-only-if-no-other-script为处理动作,可以有-e参数指定多个
-
input-file为输入文件,可以指定多个
* 注:如果省略input-file,则从标准输入读取内容
说明
sed命令会对输入文件或者管道输入逐行处理,言外之意如果使用sed命令打印文件的第2行,sed不会直接访问该行,而是从第1行逐步处理下来,并且也不会处理完第2行之后结束命令,还会将剩余的内容逐步处理完毕。要真正掌握好sed命令,而不是局限于“增删改查”这样基本的用法,读者需要了解sed命令中两个重要概念:
-
模式空间(pattern space)
-
模式空间是sed命令中的主要工作空间,用于存储、处理当前的文本行。
-
每当sed读取一行文本时,将文本行存储在模式空间中,sed在模式空间中对文本进行处理和操作。
-
模式空间中的文本可以通过命令进行修改、替换、删除、插入等操作,然后根据需要将最终的文本输出。
-
每处理完一行都会清理模式空间,而后处理下一行。
*注:如果sed中使用-e运行多个命令,那该行会在所有-e执行完后才会在模式空间中清理掉
-
保持空间(hold space)
-
保持空间是sed命令中的一个辅助空间,用于存储临时的数据或文本。
-
所有的增、删、改等操作,只能在模式空间中进行,而不能在保持空间中处理。
-
可以通过命令,来将保持空间与模式空间的数据进行互换、追加或者覆盖(将模式空间的数据追加/覆盖进保持空间;或者将保持空间的数据追加/覆盖模式空间)。
-
保持空间初始数据为“一个回车”
-
保持空间的数据会被持续保留(不会像模式空间处理下一行数据前清空),一旦保持空间与模式空间的数据交换,那保持空间仅存储互换而来的数据。
此外,sed命令实际上就是对选中的行进行一系列处理,一种扩展格式如下:
sed -n -e '[...] {a1;a2}; a3; a4' -e '[...] {a1; a2; a3}' filename
[...]表示选择行(可以不加。如果不加,代表对所有行进行操作),选择行的方式可以为[xxx , zzz] / [xxx, +yyy] / [ kkk ~ yyy] / [xxx], 其中xxx与zzz可以是数字或正则,但是yyy与kkk只能是数字,部分样例如下:
-
[1,/3/]:表示从第1行到第一个匹配到3的行(该匹配不算第1行)
-
[2,+5]:表示从第2行开始往下5行
-
[2,5]:表示从第2行到第5行
-
[1~2]:表示从第1行开始每隔2行直到标准输入的末尾(包括第1行)
*注:前后都为闭区间
关于上述参数-n与-e进一步解释,请看下一章节。
此外,
-
'[...] {a1; a2; a3}',表示如果满足[...]中条件,则执行(a1;a2;a3}命令,其它行不执行;
-
' [...] {a1; a2}; a3'表示满足[...]条件则执行{a1;a2}命令后再执行a3命令,不满足[...]条件则执行a3命令
参数
本文仅介绍常见参数,读者如果有兴趣了解所有参数,请运行"man sed"或者"sed --help"进一步查看。
-
-e:同时进行多个操作。默认使用该参数处理一个操作,如果处理多个操作:sed -e '1s/p/|/g' -e '5s/q/|/g' a.txt
*注:如果使用多个-e处理多个操作,则第一个-e也不能省略
-
-i:直接对输入文件进行操作(慎用,验证后再决定是否使用)
-
-n:抑制“ 默认对模式空间的输出”,如果需要输出到终端,请在sed中使用p/P显式打印
*注:如果没有-n选项,sed命令默认会在处理完每一行后自动将模式空间的内容输出到标准输出上
-
-E或-r:支持扩展正则表达式,通常的扩展如下:
-
+:重复1次或者多次前面的字符
-
?:重复0次或者1次前面的字符
-
|:用“或”的方式查找多个符合的字符串
-
():作为某个特定组合,比如说sed -E '/level=(fail|error)/p' file,表示在file中查找包括level=fail或者level=error的内容。如果去掉括号sed -n -E '/level=fail|error/p' file,代表查找匹配level=fail或者一行中单独匹配error的内容
-
{m,n}/{m,}/{,n}/{n}:表示重复m到n次(闭区间)/大于等于m次/小于等于n次/n次,在不用-E参数时,所有这部分花括号用法得两侧加反斜线:{m,n}
内置命令(第一部分)
本文也仅介绍常用的sed内置命令,如果想进一步了解,请运行"man sed"。
-
i:将指定的文本在输出时添加到当前行的前面,但不会追加/覆盖到模式空间。比如sed '1i abcd' a.txt是在处理a.txt第1行的时候,将文本abcd在输出时候插入到第1行前面。
-
a: 将指定的文本在输出时追加到当前行的后面,也不会 追加/覆盖到模式空间。
-
c:先清空模式空间的数据,再将指定的文本输出到终端。
-
p:打印当前模式空间中的数据,一般与-n一起使用
-
q:退出sed命令,不处理输入中剩余内容。如果没有-n选项,会打印模式空间中数据
-
Q:退出sed命令,不处理输入中剩余内容。不管有没有-n选项,都不会打印模式空间中数据
-
d:删除当前模式空间中的数据
-
s:对当前模式空间内容进行替换 's/正则/文本/'
*注:为了更方便sed中的替换,所以s命令本身也有几个参数,拓展格式为:'s/正则/文本/[参数]',其中参数有:
数字:表示对一行中第几个出现的匹配项进行替换(如果命令要求对第2个匹配abc的部分进行替换,但该行只有1个abc,那不会进行替换)
i:ignore,忽略大小写
g:global,对整行数据所有匹配内容都进行替换。如果不加g,默认只会对第一个匹配内容进行替换。
p:print,对模式空间修改后的内容进行打印
w:w filename,将模式空间修改后的内容写入到filename中
-
r:将指定文件的内容追加到输出中,但不会追加/覆盖到模式空间。比如sed '1r filename' a.txt是在处理a.txt第1行时候,将filename的内容输出到终端。
-
w:将模式空间的内容写入到某个文件中,比如sed '2w filename' a.txt代表将第二行数据写入到filename中
-
y:y/acbd/ACBD/,在当前模式空间中将a替换成A,将c替换成C,将b替换成B,将d替换成D,比如echo 'ab' | sed 'y/bcad/BCAD/'的输出是AB
用法介绍(第一部分)
-
在a.txt的第2行前加上'xxx',第4行后加上'yyy',并且最后一行替换成'xyz'
首先a.txt中内容如下:
cat a.txt
xxx1 xxxa
xxx2
xxx3 xxb
test1
xxx2
xxx4
xxx3
test2 xxcxx
xxx33 xxx44
xxx2
xxx5
sed命令如下:
sed -e '2i xxx' -e '4a yyy' -e '$c xyz' a.txt
-
删除第2行数据到匹配test的数据
sed '2,/test/d' a.txt
-
将a.txt的第5行中2替换成22,并打印替换后信息
sed -n '5s/2/22/p' a.txt
*注,如果不使用-n参数与内置p命令,结果如下:
-
打印匹配/xxx2/到匹配/xxx3/的数据
读者如果观察源文件,会发现内容中有好几个匹配/xxx2/与/xxx3/的行,那么,到底sed是怎么处理的呢?
假如直接写命令 sed -n '/xxx2/,/xxx3/p' a.txt,是匹配第一个/xxx2/到第一个/xxx3/还是第一个/xxx2/到最后一个/xxx3/,还是每满足一次/xxx2/到/xxx3/都打印出来?
我们先看上述命令的运行结果:
基于上述结果,我们可以猜测首先不是打印的第一个/xxx2/到第一个/xxx3/,也很明显不是打印的第一个/xxx2/到最后一个/xxx3/,看起来像是每碰到一次/xxx2/与/xxx3/都会把之间的内容打印出来,但为何会打印了最后一行xxx5呢?
所以猜测对于[xxx,yyy],如果yyy不存在或者在xxx后不匹配,那代表是xxx开始往下的所有行都会进行处理。为了验证这一点,比如说我们打印a.txt中匹配/xxx2/与/abcdef/(不匹配/xxx2/后任一行)之间的行,看结果会如何。
果然,将第一个/xxx2/的行一直到最后一行的数据,都打印了出来。我们再换一种,打印从第7行开始直到匹配/abcdef/的行:
同样,如果执行sed -n '/xxx2/,/xxxa/p' a.txt
上述案例中,尽管/xxxa/出现在了第1行,但因为/xxx2/最早出现在第2行,也就是第二个正则在第一个正则匹配到的行的下方不能匹配到行(尽管在上方能匹配一行),这种情况下sed仍然会处理从第一个匹配/xxx2/的行到最后一行。
所以通过如上几个案例,也基本证实了最开始的猜测,如果通过[数字/正则, 正则]形式取行,假如第二个正则如果不存在,那命令会持续到文本结束。不过对[a,+b]或者[a~b]这两种取行而言,不存在此种混淆,因为b只能是数字,没有可能匹配到多行的情况。
但需要注意的是,如果sed -n '3,1p' a.txt,处理的是第3行数据,而不是从第3行开始往下所有行的数据。
我们将该部分内容总结如下:
1. [数字]:不会混淆,仅针对数字指定的行数进行处理。如果数字大于标准输入的行数,那sed不会对任何行进行处理(但会扫描标准输入的每一行)*注:数字大于等于1,下同
2. [正则]:不会混淆,仅针对正则能匹配到的所有行进行处理。如果正则匹配不到任何行,那sed不会对任何行进行处理(但会扫描标准输入的每一行)
3. [数字1, 数字2]:不会混淆,仅针对数字1到数字2之间的行进行处理(闭区间)
-
如果数字1大于标准输入的行数,那sed不会对任何行进行处理(但会扫描标准输入的每一行)
-
如果数字1小于等于标准输入的行数,但数字2大于标准输入的行数,那sed处理从数字1到标准输入的最后一行数据
-
如果数字2小于等于数字1,那sed不会报错,并且只会处理数字1对应的行