文本处理三剑客之sed

来源:互联网 发布:免费淘宝账号和密码 编辑:程序博客网 时间:2024/05/20 02:29

个人博客地址: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 sedtestI 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 orangeI 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]#lltotal 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]#lltotal 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'12
  • 打印第2行到第5行数据
[root@localhost sed]#seq 10 | sed -n '2,5p'2345
  • 在第2行后面插入“hello”
[root@localhost sed]#seq 3 | sed '2ahello'12hello3[root@localhost sed]#seq 3 | sed '2a\hello'12hello3
  • 在sedtest这个文本文件中,在所有apple 字样前面加入“Hello World”
[root@localhost sed]#sed '/apple/i\Hello World' sedtestI 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 WorldApple-pen~Pineapple-pen(Uhh\h~)Hello World   Pen-Pineapple-Apple-pen!Hello WorldPen-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' sedtestI 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" sedtestI 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命令在处理标准输入的过程中,大部分的情况下是在模式空间中完成的。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%的应用。由于文中例子以及介绍,均是笔者本人学习过程中的理解以及查阅资料所得,难免有失误之处,欢迎读者留言并指出错误所在,我会及时更正。