个人博客地址:http://www.pojun.tech/ 欢迎访问
本文部分示例引用自 http://www.gnu.org/software/sed/manual/sed.html 。这是sed的官方帮助手册,如果想查看完整示例,可移步此网址,查看sed的详细介绍。
sed 简介
sed(Stream EDitor)是一种流编辑器,也被称为行编辑器,它一次处理一行内容。
处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。然后读入下行,执行下一个循环。如果没有使诸如‘D’的特殊命令,那会在两个循环之间清空模式空间,但不会清空保留空间。这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。sed 命令通常用于以下几个场景
- 编辑比较大的文本文件
- 编辑命令太复杂,交互式文本编辑器中不好操作
- 简化对文件的反复操作,编写转换程序
简单的示例
首先我们先来看一个示例,在这个示例中,我们将使用sed 来完成一些简单的事情,其中具体的语法细节会在后面的内容中详细讲解,这里就先演示以下sed的强大魅力。
下面的演示将以这段文本作为基础
[root@localhost sed]#cat sedtest
I have a pen,
I have an apple.
(Uhh~)Apple-pen!
I have a pen,
I have pineapple.
(Uhh~)Pineapple-pen!
Apple-pen~Pineapple-pen(Uhh\h~)
Pen-Pineapple-Apple-pen!
Pen-Pineapple-Apple-pen!
我们在每行的开头加上一个
#
号,也就是将每一行都注释掉
[root@localhost sed]#sed 's/^/#/g' sedtest
#I have a pen,
# I have an apple.
#(Uhh~)Apple-pen!
#I have a pen,
# I have pineapple.
#(Uhh~)Pineapple-pen!
#Apple-pen~Pineapple-pen(Uhh\h~)
# Pen-Pineapple-Apple-pen!
#Pen-Pineapple-Apple-pen!
当然,我们也可以轻松地在每一行的结尾加上我们喜欢的字符
$
,请先不要疑惑,如果对正则表达式不是很理解的朋友可以这样来理解,第一个$
表示的是行尾,第二个$
表示将要准备替换为的字符。详细的内容我们会在接下来介绍。
[root@localhost sed]#sed 's/$/$/g' sedtest
I have a pen,$
I have an apple.$
(Uhh~)Apple-pen!$
I have a pen,$
I have pineapple.$
(Uhh~)Pineapple-pen!$
Apple-pen~Pineapple-pen(Uhh\h~)$
Pen-Pineapple-Apple-pen!$
Pen-Pineapple-Apple-pen!$
我们在文本的第一行之前插入一行,
I have an orange
[root@localhost sed]#sed '1 i I have an orange' sedtest
I have an orange
I have a pen,
I have an apple.
(Uhh~)Apple-pen!
I have a pen,
I have pineapple.
(Uhh~)Pineapple-pen!
Apple-pen~Pineapple-pen(Uhh\h~)
Pen-Pineapple-Apple-pen!
Pen-Pineapple-Apple-pen!
当然啦,能增加肯定就可以删除了,所以接下来我们将文本中所有包含
pen
字眼的行删掉。
[root@localhost sed]#sed '/pen/d' sedtest
I have an apple.
I have pineapple.
注意: 我们在实例中进行的所有操作,并没有真正的改变原文本文件的内容,只是在输出的内容有了变化,这一点我们将会进行介绍。
sed 的用法
sed 是一个强大的工具,通过之前的示例,我们可以看出,sed可以实现很多的功能,实际上它也是结合了正则表达式来完成的所有的操作,这一点与grep很相似。因此,如果想要使用好sed 这把利器,掌握好正则表达式是非常有必要的。关于正则表达式的使用,在我之前的一片博客中已经有了详细的介绍,《Linux 基础命令(五)—— 文本处理三剑客之grep》,可以参照这篇博客去了解详细的使用。下面的介绍中,我们将不再赘述正则,而是直接使用。
sed 命令的常用用法如下
sed [option]... [SCRIPT] [INPUTFILE ...]
常用的命令行选项 OPTIONS
与许多命令一样,sed命令也有着众多繁而复杂的选项,合理的使用这些选项会产生意想不到的效果。下面介绍一些常用的选项。
-n, --quiet, --silent
默认情况下,sed命令会将它处理的每一行的内容打印到标准输出,使用这个选项可以禁止输出。
-e script
将脚本添加到命令中执行。可以理解为多点编辑。就是使用 -e 选项 加上脚本内容 ,可以使sed命令实现一个命令,匹配多个脚本,比如 sed -e '/pen/d' -e 's/$/$/g' sedtest
-f script-file
从指定的脚本文件中,读取出脚本内容,并合并到sed COMMAND中进行执行。通俗的理解为将 脚本写在了文件中。
-r
支持扩展的正则表达式,与egrep一样,扩展的正则表达式更利于阅读,没有那么多的斜杠,利于书写。
-i
直接修改源文件。如果在命令中加入了 -i 选项,源文件的内容将会被直接修改掉。
-i[SUFFIX]
这还是-i 选项,这个选项的意思是说,如果在使用 该选项的过程中指定了后缀名称,那么该命令在执行的过程中会先将源文件备份成以[SUFFIX] 为后缀的文件,然后对文件进行操作
接下来,我们还是以文章开头提到的文件来进行测试,来解释这些选项的含义。
- 将文本中所有包含
pen
字样的行删除掉,同时将apple
字样替换成orange
。
[root@localhost sed]#sed -e '/pen/d' -e 's/apple/orange/g' sedtest
I have an orange.
I have pineorange.
- 创建脚本文件 scritp1 并在里面写入 脚本 。
#在script1 文件中写入符合正则表达式的脚本
[root@localhost sed]#echo "/pen/d" &> script1
#查看脚本文件内容
[root@localhost sed]#cat script1
/pen/d
#执行sed命令
[root@localhost sed]#sed -f script1 sedtest
I have an apple.
I have pineapple.
这种以读取脚本文件来处理文本文件的方式,很适合我们在处理复杂文件的时候使用,实际生产中,我们往往会遇到大量的复杂的文本文件需要进行处理,sed命令可以很好的解决。例如日常大量的日志。
#在使用-i 选项的同时,指定备份的后缀名称
[root@localhost sed]#sed -i.bak -f script1 sedtest
#列举出当前列表下的文件
[root@localhost sed]#ll
total 12
-rw-r--r--. 1 root root 7 Aug 11 21:03 script1
-rw-r--r--. 1 root root 37 Aug 11 21:08 sedtest
-rw-r--r--. 1 root root 188 Aug 11 20:02 sedtest.bak
#查看修改后的文件
[root@localhost sed]#cat sedtest
I have an apple.
I have pineapple.
#查看备份文件
[root@localhost sed]#cat sedtest.bak
I have a pen,
I have an apple.
(Uhh~)Apple-pen!
I have a pen,
I have pineapple.
(Uhh~)Pineapple-pen!
Apple-pen~Pineapple-pen(Uhh\h~)
Pen-Pineapple-Apple-pen!
Pen-Pineapple-Apple-pen!
我们前面曾经介绍过,sed 命令的执行结果默认是不会修改源文件,而是直接输出到标准输出的。接下来,我们将处理后的结果保存到一个新的文件中。
- 不使用
-i
选项,将修改后的文本保存到另外一个文件中。
#使用IO重定向 轻松实现文件另存为
[root@localhost sed]#sed '/pen/d' sedtest &> sedtest2
[root@localhost sed]#ll
total 16
-rw-r--r--. 1 root root 7 Aug 11 21:03 script1
-rw-r--r--. 1 root root 188 Aug 11 20:02 sedtest
-rw-r--r--. 1 root root 37 Aug 11 21:25 sedtest2
[root@localhost sed]#cat sedtest2
I have an apple.
I have pineapple.
当然,在实际使用中,我们通常是会直接修改源文件的,这样的话,直接使用相应的选项就可以了。
sed 脚本概述
在前面的例子中,我们已经见识到了sed 命令结合正则表达式脚本 所产生的强大的效果。sed命令的脚本其实是比较复杂的,它不光可以写正则表达式,更可以使用地址定界+命令选项的方式来实现我们想要实现的功能。
在sed中,我们将正则表达式也看作一种地址定界
脚本的书写格式是 ** ‘地址命令’**
sed命令中支持的地址定界
定界类型 | 作用 |
---|---|
不给地址 | 对全文进行处理 |
# | 指定的行 |
/pattern/ | 能够被模式匹配到的每一行 |
#,# | 从第n行到第m行 |
#,+# | 从第n行,加上其后面m行 |
/pat1/,/pat2/ | 符合第一个模式和第二个模式的所有行 |
#,/pat1/ | 从第n行到符合 /pat1/ 这个模式的行 |
1~2 | ~ 这个符号表示步进,1~2 表示的是奇数行 |
2~2 | 2~2 表示的是偶数行 |
sed 命令中常用的编辑命令
定界类型 | 作用 |
---|---|
d | 删除模式空间匹配的行,并立即启用下一轮循环 |
p | 打印当前模式空间内容,追加到默认输出之后 |
q | 读取到指定行之后退出 |
a [\]text | 在指定行后面追加文本,支持使用\n实现多行追加 |
i [\]text | 在行前面插入文本 |
c [\]text | 替换行为单行或多行文本 |
w /path/somefile | 保存模式匹配的行至指定文件 |
r /path/somefile | 读取指定文件的文本至模式空间中匹配到的行后 |
= | 为模式空间中的行打印行号 |
! | 模式空间中匹配行取反处理 |
下面我们使用几个简单的示例来演示这些常用选项的使用。
- 读取到第二行的时候退出命令
[root@localhost sed]#seq 3 | sed '2q'
1
2
- 打印第2行到第5行数据
[root@localhost sed]#seq 10 | sed -n '2,5p'
2
3
4
5
- 在第2行后面插入“hello”
[root@localhost sed]#seq 3 | sed '2ahello'
1
2
hello
3
[root@localhost sed]#seq 3 | sed '2a\hello'
1
2
hello
3
- 在sedtest这个文本文件中,在所有apple 字样前面加入“Hello World”
[root@localhost sed]#sed '/apple/i\Hello World' sedtest
I have a pen,
Hello World
I have an apple.
(Uhh~)Apple-pen!
I have a pen,
Hello World
I have pineapple.
Hello World
(Uhh~)Pineapple-pen!
Hello World
Apple-pen~Pineapple-pen(Uhh\h~)
Hello World
Pen-Pineapple-Apple-pen!
Hello World
Pen-Pineapple-Apple-pen!
使用 s/// 查找替换
使用 s/// 查找替换实际上也是sed 脚本的一部分,在vim中也有相应的使用。支持使用其他的分隔符s@@@,s###。
常用的有如下的替换标记:
- g: 行内全局替换
- p: 显示替换成功的行
- w /PATH/TO/SOMEFILE:将替换成功的行保存至文件中
下面实现一个特殊的示例,在所有的apple单词后面加上Hello,在所有的pen单词前面加上World
[root@localhost sed]#sed -e 's/apple/&Hello/g' -e 's/pen/World&/g' sedtest
I have a Worldpen,
I have an appleHello.
(Uhh~)Apple-Worldpen!
I have a Worldpen,
I have pineappleHello.
(Uhh~)PineappleHello-Worldpen!
Apple-Worldpen~PineappleHello-Worldpen(Uhh\h~)
Pen-PineappleHello-Apple-Worldpen!
Pen-PineappleHello-Apple-Worldpen!
示例中的
&
符号可以理解为前一个 模式&
的位置,就是配置之后前一个模式的的位置。
这个示例还可以有另外一种实现方式,就是使用正则表达式的分组功能。
#使用正则表达式式的分组功能,可以实现同样的功能
[root@localhost sed]#sed -e 's/\(apple\)/\1Hello/g' -e 's/\(pen\)/World\1/g' sedtest
#也可以使用扩展的正则表达式
[root@localhost sed]#sed -r -e 's/(apple)/\1Hello/g' -e 's/(pen)/World\1/g' sedtest
s///
替换命令的使用方式还可以很灵活,可以分别指定让哪些行进行匹配,使用的时候可以灵活应对。
将第1行至第5行的apple字样替换为HELLO,将每一行的第二个en单词替换成EN,第一个不理会
# s///替换命令可以写在一个脚本里面
[root@localhost sed]#sed "2,5s/apple/Hello/g; s/en/EN/2g" sedtest
I have a pen,
I have an Hello.
(Uhh~)Apple-pen!
I have a pen,
I have pineHello.
(Uhh~)Pineapple-pen!
Apple-pen~Pineapple-pEN(Uhh\h~)
Pen-Pineapple-Apple-pEN!
Pen-Pineapple-Apple-pEN!
高级编辑命令以及模式空间和保持空间
我们前面已经介绍了很多关于sed命令的使用,在实际的使用中,这些命令的组合基本上可以完成大部分的日常工作。sed本身其实还有还多其他的高级编辑命令和使用方式,这就是接下来要介绍的内容。
模式空间和保持空间
我们在前面的介绍中,或多或少地提到了模式空间。模式空间,顾名思义,模式,通常就是指的我们的正则匹配,空间则可以理解为一段内存空间,也就是符合我们正则匹配的内容所在的一个空间。保持空间,则可以理解为临时备份空间,用来存储模式空间中部分数据,并与模式空间配合完成复杂的内容处理。 我们使用下面这张图来简要的说明一下模式空间和保持空间的联系
sed命令在处理标准输入的过程中,大部分的情况下是在模式空间中完成的。sed本身是一个基于stream的行编辑器,它对文本的处理方式就是以行为单位,一行一行的来处理的。也就是说,先读取一行到模式空间中,判断是否匹配模式,如果是进行处理,否则,不处理。 了解了模式空间和保持空间之后,我们可以结合一些复杂的命令来进行复杂的操作。
高级命令使用
命令 | 命令作用 |
---|---|
D | 从模式空间中删除行直到第一个换行符,然后重新启动循环。如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本,并不会读取新的输入行,而使用合成的模式空间重新启动循环。如果模式空间不包含换行符,则会像发出d命令那样启动正常的新循环 |
G | 将行从保持空间追加到模式空间,并在其前面加上一个换行符。 |
H | 将模式空间的行附加到保持空间,并在其前面加上换行符。 |
N | 读取匹配到的行的下一行追加至模式空间。 |
P | 打印模式空间开端至\n内容,并追加到默认输出之前 |
d | 删除模式空间中的行 |
g | 从保持空间取出数据覆盖(替换)至模式空间 |
h | 把模式空间中的内容覆盖(替换)至保持空间中 |
l | 以明确的形式打印模式空间的内容,每行的末尾都会标有$ |
n | 读取匹配到的行的下一行覆盖至模式空间 |
x | 把模式空间中的内容与保持空间中的内容进行互换 |
下面的示例很好的说明了N,D的使用
[root@localhost sed]# seq 6 | sed -n 'N; l; D'
1\n2$
2\n3$
3\n4$
4\n5$
5\n6$
l
命令能够明确的打印模式空间里面的内容- sed 首先将第一行读入模式空间(即“1”)。
- 在每个循环开始时,N 命令将附加一个换行符和下一行到模式空间(即’1’,’\ n’,’2’在第一个周期)。
- D命令将模式空间中第一个
\n
以及之前的内容全部删除掉(模式空间中只剩下内容2)然后启动另外一个循环。- 在下一个循环中,N命令将附加一个换行符,并将下一个输入行添加到模式空间(例如“2’,’\ n’,’3“)。
下面列举了一些简单的例子,并配有相应的注释,可以结合结合起来理解
$
符号表示每个文件的最后一行!
符号表示取反
# 打印偶数行
sed -n 'n;p' FILE
# 将文件内容倒序输出 类似于 tac命令
sed '1!G;h;$!d' FILE
#打印出最后一行
sed 'N;D' FILE
# 打印最后两行
sed '$!N;$!D' FILE
# 除了最后一行,其余全删掉 也就是打印最后一行
sed '$!d' FILE
# 每行文字中间插入一个空白行
sed 'G' FILE
# 每一行文字都被空白行替换掉
sed 'g' FILE
# 删除多余的空白行,保证每行文字之间只有一个空白行
sed '/^$/d;G' FILE
# 输出奇数行
sed 'n;d' FILE
通过上面的介绍,基本上说明了sed的用法。已经能够解决日常生产中90%的应用。由于文中例子以及介绍,均是笔者本人学习过程中的理解以及查阅资料所得,难免有失误之处,欢迎读者留言并指出错误所在,我会及时更正。