本文部分示例引用自 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$
  1. l 命令能够明确的打印模式空间里面的内容

  2. sed 首先将第一行读入模式空间(即“1”)。

  3. 在每个循环开始时,N 命令将附加一个换行符和下一行到模式空间(即’1’,’\ n’,’2’在第一个周期)。

  4. D命令将模式空间中第一个\n以及之前的内容全部删除掉(模式空间中只剩下内容2)然后启动另外一个循环。

  5. 在下一个循环中,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/ 欢迎访问