本文部分示例引用自 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%的应用。由于文中例子以及介绍,均是笔者本人学习过程中的理解以及查阅资料所得,难免有失误之处,欢迎读者留言并指出错误所在,我会及时更正。
个人博客地址:http://www.pojun.tech/ 欢迎访问