何为sed
sed
是Stream Editor的简称,是Linux自带的一个文本处理工具,以行为单位,对文本进行处理,可以对每行的内容做替换、删除、新增、选取等操作。
sed
在处理文本时,会把当前行读入到缓冲区,这个缓冲区称为模版块(Pattern Space),然后用相关命令处理模板块的内容,处理完成后,将缓冲区的内容输出,直到文件末尾。此外sed
也提供了临时缓冲区(Hold Space),用于实现更加复杂的文本操作。
sed
命令行格式为:
sed [option]... {script-only-if-no-other-script} [input-file]...
option
为选项,后面接命令,再后面接输入文件。
命令
sed
单个命令的语法为:
[n1[,n2]]<function>
n1
和n2
可选,代表选择进行function
操作的前置条件,n1
和n2
有多种取值方式:
- 当取值为数字或
$
时,代表行号的限定范围,$
表示最后一行,例如:
从第10行开始,进行function
操作,直到第20行,那么可以写为:10,20<function>
从第10行开始,进行function
操作,可以写为:10<function>
或10,$<function>
从第1行开始,进行function
操作,直到第10行,可以写为:1,10
- 当取值为正则表达式时,代表行需要满足正则表达式才会进行
function
的相关操作,例如:
从第10行开始,对不以Hello
开头的行进行function
操作,可以写为10,/^Hello/<function>
- 当取值为包含
!
时,表示在一定条件下不执行相关操作,例如:
不对第1到4行的文本执行function
操作,可以写为:1,5!<function>
此外sed
可以接受多个命令,可以用;
隔开,或者用多个-e
选项即可,在处理模版块时会按照先后依次顺序执行。
在介绍function
之前,为了方便演示,可以新建一个文件testfile
,并写入以下内容:
Line 1
Line 2
...
Line 10
或者使用Shell自动生成:
echo > testfile
for i in {1..10}
do
echo "Line $i" >> testfile
done
基本function
的种类:
-
a
:在当前行下面插入文本。
后面可以接字符串,这个字符串会插入到当前行的下一行,例如在第8行后插入字符串helloworld
:sed '8ahelloworld' testfile
或者从第8行开始,在每行后面插入一个
helloworld
sed '8,$a helloworld',
-
i
:在当前行上面插入文本
类似于a
,后面接字符串,这个字符串会插入到当前行的上一行,例如在第8行之前插入字符串helloworld
:sed '8ihelloworld' testfile
-
c
:把当前的行替换为新的文本
例如把第八行的Line 8
替换为helloworld
:sed '8chelloworld' testfile
-
d
:删除当前的行
例如删除第八行的文本Line 8
删除:sed '8d' testfile
或者删除第5到第8行文本:
sed '5,8d' testfile
删除包含字符串
Line 8
的行:sed '/Line 8/d' testfile
-
s
:替换指定的字符串,语法规则如下:sed 's/Regexp/Replacement/Flags' file
s
命令将符合正则表达式Regexp
的行全部替换成Replacement
,最后的字符Flags
为标志选项,有以下几种:g
:用Replacement
替换模版空间中所有匹配Regexp
的部分,则不仅仅是第一个匹配部分
例如要将testfile
中的单词Line
全部替换成Word
,那么可以写作:sed 's/Line/Word/g' testfile
- 1到9的数字
N
:只用Replacement
替换模版空间中,忽略第0
到N - 1
个匹配Regexp
的部分,然后将匹配Regexp
的部分输出。
例如,下面案例会将Line
替换为Lixx
:sed 's/[a-z]/x/2g' testfile
p
:若发生了替换操作,则输出模版空间中修改后的行数据,和p
命令很类似w file-path
:若发生了替换操作,将模板块中的数据写入到文件file-path
中
例如下面示例会将从第5行开始,替换Line
为Word
,并将替换后的结果输出到当前目录的outputfile
文件,直到文件末尾:sed -n '5,$s/Line/Word/gw outputfile' testfile
i
:与Regexp
匹配时,不区分大小写
例如,下面命令可以将Line
替换成Word
:sed 's/line/Word/ig' testfile
此外,也可利用
s
在每行头部或者尾部添加字符串,可以利用字符^
表示行头,字符$
表示行尾
比如在每行行头添加单词Hello
:sed 's/^/Hello/g' testfile
在行尾添加单词
Hello
:sed 's/$/Hello/g' testfile
当然
s
前面也可以加上行范围 -
p
:打印当前模板块空间的所有内容(模板块的定义前面已经提到),并追加到默认输出之后
例如在每行后面追加一个单词number
,并将修改前和修改后的结果打印出来(-n
表示不打印默认输出):sed -n 'p;s/$/number/g;p' testfile
或者从第8行开始,将以
Line
开头的行打印出来sed '5,/Line/p' testfile
其他function
种类:
-
n
:读取下一个输入行,用下一个命令处理新的行而不是使用第一个命令 -
N
:追加下一个输入行到模板块的后面并在中间嵌入一个新行,可以利用该命令将多个行的内容整合为一行。
例如下列命令可以将三行语句整合为一行:sed 'N;N;s/\n/ /g' testfile
逻辑就是就是读取三行后,然后将换行符
\n
替换为空格。
或者可以输出行号为奇数的行:sed -n '$!N;P' testfile
-
P
:打印当前模板块空间的第一行
模板块和临时缓冲区(Hold Space)的操作:
h
:拷贝模板块的内容到临时缓冲区H
:追加模板块的内容到临时缓冲区D
:删除模板块的第一行(意味着这个被删除的部分不会传至标准输出),并放弃之后的命令,然后对新读入的内容,重头执行sed
。g
:取得临时缓冲区的内容,并替换当前模板块中的文本G
:取得临时缓冲区的内容,并追加到当前模板块中文本的后面
可以利用sed
的临时缓冲区执行更加复杂的文本修改操作,举一个经典的例子:
实现倒序输出,即从最后一行开始逐行输出到第一行
思路:要实现倒序输出,我们需要在读取每一行的时候,就将行的内容放在模板块的前面。所以在读第一行的时候,需要将第一行的文本从模式块读到临时缓冲区,然后再读第二行的时候,把临时缓冲区中第一行的数据追加到模版块,依次类推。只需要对第一行和最后一行的做特殊处理即可。
sed '1!G;h;$!d' testfile
1!G
表示不对第一行做G
操作,$!d
表示不对最后一行做d
操作
执行过程示例图:
其他操作:
I
:列出不能打印字符的清单q
:退出sed
选项
option
参数主要包含以下几个:
-n
或--quiet
或--silent
:仅显示被sed
特殊处理的行,如果不加,那么所有来自输入文本的数据都会被输出,不论有没有进行过修改。-e <script>
或--expression=<script>
:使用script
,也可以省略掉-e
-f <script-file>
或--file=script-file
:指定存放script
的文件-i[suffix]
或--in-place[=suffix]
:如果不加suffix
,那么直接修改文件的内容,不输出到终端。如果加了suffix
,则会在处理前,对文件进行备份(复制),备份文件名会在原先文件名后面加上suffix
,然后再对原文件进行修改-l <number>
或--line-length=<number>
:--posix
:禁用的GNU扩展功能,例如replacement
选项中中的\L
、\l
、\U
、\u
、\E
。-r
或--regexp-extended
:让sed
支持扩展的正则表达式-s
或--separate
:在处理多个文件时,对它们依次单独处理,而不是视为一个整体-u
或--unbuffered
:从输入文件中加载尽可能少的行,并更频繁地刷新输出缓冲区,适合机器内存比较紧张的情况-z
或--null-data
:每行按照\0
分隔,而不是换行符。--help
:查看sed
帮助--version
:查看sed
版本