12、sed文本处理

 

 

1、概述

1.sed 是一款流编辑工具,用来对文本进行过滤与替换工做,特别是当你想要对几十个配置文件作统计修改时,你会感觉到 sed 的魅力!
sed 经过输入读取文件内容,但一次仅读取一行内容进行某些指令处理后输出,因此 sed更适合于处理大数据文件。
2.sed 流程:
* 经过文件或管道读取文件内容。
* sed 并不直接修改源文件,而是将读入的内容复制到缓冲区中,咱们称之为模式空间(pattern space)。
* 根据 sed 的指令对模式空间中的内容进行处理并输出结果,默认输出至标准输出即屏幕上。html

 

 

2、sed 语法和基本命令



 

基本用法

 

sed [options] {sed-commands} {input-file}

 

 sed 每次从 input-file 中读取一行记录,并在该记录上执行 sed-commands
sed 首先从 input-file 中读取第一行,而后执行全部的 sed-commands;再读取第二行,执行全部 sed-commands,重复这个过程,直到 input-file 结束
经过制定[options] 还能够给 sed 传递一些可选的选项
node

 

 

 

常见命令选项;改变数据处理的方式

 

 

命令选项    注释
-n    屏蔽默认输出(所有文本),再也不默认显示模式空间中的内容
-i,--in-place      直接修改源文件内容
-f    -f script-file,--file=script-file              从文件中读取脚本指令,对编写自动脚本程序来讲很棒!
-e SCRIPT -e SCRIPT:能够同时执行多个脚本
-r    启用扩展的正则表达式,若与其余选项一块儿使用,应做为首个选项
{}    可组合多个命令,以分号分隔
--version                     显示 sed 版本。
--help                           显示帮助文档。
-l N, --line-length=N       该选项指定 l 指令能够输出的行长度,l 指令用于输出非打印字符。
--posix                     禁用 GNU sed 扩展功能。

 

下面的例子演示了 sed 的基本语法,它打印出/etc/passwd 文件中的全部行
web

sed –n 'p' /etc/passwd


该例子的重点在于, {sed-commands}既能够是单个命令,也能够是多个命令。你也能够把多个 sed 命令合并到一个文件中,这个文件被称为 sed 脚本,而后使用-f 选项调用它,以下面的例子:
使用 sed 脚本的基本语法:
正则表达式

sed [ options ] –f {sed-commands-in-a-file} {input-file}

 


下面的例子演示了使用 sed 脚本的用法,这个例子将打印/etc/passwd 问中以 root nobody
开头的行:
shell

vi test-script.sed
/^root/ p
/^nobody/ p
$ sed –n –f test-script.sed /etc/passwd

 


你也可使用 –e 选项,执行多个 sed 命令,以下所示:
-e 的使用方法
express

sed [ options ] –e {sed-command-1} –e {sed-command-2} {input-file}

 

下面的例子演示了-e 的使用方法,它打印/etc/passwd 中以 root nobody 开头的行:vim

[root@ceph-node5 ~]# sed -n -e '/^root/ p' -e '/^nobody/ p' /etc/passwd 

 


若是使用-e 执行多个命令,也可使用反斜杠\把它们分割到多行执行:
缓存

sed -n \
-e '/^root/ p' \
-e '/^nobody/ p' \
/etc/passwd

 


也可使用{ }将多个命令分组执行:
{}的使用方法:
bash

sed [options] '{
sed-command-1
sed-command-2
}' input-file

 


下面的例子演示了{}的使用方法,打印/etc/passwd 中以 root nobody 开头的行:
app

sed -n '{
/^root/ p
/^nobody/ p
}' /etc/passwd

 


注意:sed 毫不会修改原始文件 input-file,它只是将结果内容输出到标准输出设备。 若是要保持变动,应该使用重定向 > filename.txt

 

 

Sed 脚本执行流程

Sed 脚本执行听从下面简单易记的顺序: Read,Execute,Print,Repeat(读取,执行,打印,重复)
简称 REPR
分析脚本执行顺序:

读取一行到模式空间(sed 内部的一个临时缓存,用于存放读取到的内容)
在模式空间中执行命令。若是使用了{ } 或 –e 指定了多个命令, sed 将依次执行每一个命令
打印模式空间的内容,而后清空模式空间
重复上述过程,直到文件结束

 

如图:

 

 

 

 

打印模式空间(命令 p)

 

 

全部的示例都要用到下面的 employee.txt 文件,请先行建立该文件。

[root@ceph-node5 ~]#  vim employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

使用命令 p,能够打印当前模式空间的内容。
sed 在执行完命令后会默认打印模式空间的内容,既然如此,那么你可能会问为什么还须要命令 p 呢。
有以下缘由,命令 p 能够控制只输出你指定的内容。一般使用 p 时,还须要使用-n 选项来
屏蔽 sed 的默认输出,不然当执行命令 p 时,每行记录会输出两次。

[root@ceph-node5 ~]# sed 'p' employee.txt
101,John Doe,CEO
101,John Doe,CEO
102,Jason Smith,IT Manager
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
105,Jane Miller,Sales Manager

 

输出 employee.txt 的内容,只打印一行(cat employee.txt 命令做用相同):

[root@ceph-node5 ~]# sed -n 'p' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

定址符

 

 

定址符,即[地址1,[地址2]]
是可选项,用来指定处理的起、止行数
不指定定址符时默认处理所有文本
定址符的表示方式可使用行号和正则表达式

 

 例如:

Address:
1、StartLine,EndLine
    好比1,100
    $:最后一行
2、/RegExp/
    /^root/
3、/pattern1/,/pattern2/
    第一次被pattern1匹配到的行开始,至第一次被pattern2匹配到的行结束,这中间的全部行
4、LineNumber
    指定的行
5、StartLine, +N
    从startLine开始,向后的N行;

 

若是在命令前面不指定地址范围,那么默认会匹配全部行。 下面的例子,在命令前面指定了地址范围:

只打印第 2 :

[root@ceph-node5 ~]#  sed -n '2p' employee.txt  
102,Jason Smith,IT Manager

 


打印第 1 至第 4 :

[root@ceph-node5 ~]# sed -n '1,4 p' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 


打印第 2 行至最后一行($表明最后一行):

[root@ceph-node5 ~]#  sed -n '2,$ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

修改地址范围

 

可使用逗号、加号、和波浪号来修改地址范围。
上面的例子里面,就已经使用了逗号参与地址范围的指定。其意思很明了: n,m 表明第 n 至第 m 行。
加号+配合逗号使用,能够指定相的若干行,而不是绝对的几行。如 n,+m 表示从第 n 行开始后的 m
波浪号~也能够指定地址范围。 它指定每次要跳过的行数。如 n~m 表示从第 n 行开始,每次跳过 m 行:

1~2 匹配 1,3,5,7,……
2~2 匹配 2,4,6,8,……
1~3 匹配 1,4,7,10,…..
2~3 匹配 2,5,8,11,…..

 

 只打印奇数行

 

[root@ceph-node5 ~]# sed -n '1~2 p' employee.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
105,Jane Miller,Sales Manager

 


模式匹配


一如可使用数字指定地址(或地址范围),也可使用一个模式(或模式范围)来匹配,以下面的例子所示。
打印匹配模式"Jane"的行:

[root@ceph-node5 ~]# sed -n '/Jane/ p' employee.txt
105,Jane Miller,Sales Manager

 


打印第一次匹配 Jason 的行至第 4 行的内容:

[root@ceph-node5 ~]#  sed -n '/Jason/,4 p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 



注意:若是开始的 4 行中,没有匹配到 Jason,那么 sed 会打印第 4 行之后匹配到 Jason 的内容


打印从第一次匹配 Raj 的行到最后的全部行:

[root@ceph-node5 ~]# sed -n '/Raj/,$ p' employee.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


打印自匹配 Raj 的行开始到匹配 Jane 的行之间的全部内容:

[root@ceph-node5 ~]# sed -n '/Raj/,/Jane/ p' employee.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 



打印匹配 Jason 的行和其后面的两行:

[root@ceph-node5 ~]#  sed -n '/Jane/,+2 p' employee.txt
105,Jane Miller,Sales Manager

 

 

删除行

 

命令 d 用来删除行,须要注意的是它只删除模式空间的内容,和其余 sed 命令同样,命令 d不会修改原始文件的内容。
若是不提供地址范围, sed 默认匹配全部行,因此下面的例子什么都不会输出,由于它匹配了全部行并删除了它们:

sed 'd' employee.txt


指定要删除的地址范围更有用,下面是几个例子:
只删除第 2 :

[root@ceph-node5 ~]# sed '2 d' employee.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


删除第 1 至第 4 行:

[root@ceph-node5 ~]# sed '1,4 d' employee.txt
105,Jane Miller,Sales Manager

 

删除第 2 行至最后一行:

[root@ceph-node5 ~]# sed '2,$ d' employee.txt
101,John Doe,CEO

 


只删除奇数行:

[root@ceph-node5 ~]#  sed '1~2 d' employee.txt
102,Jason Smith,IT Manager
104,Anand Ram,Developer

 



删除匹配 Manager 的行:

[root@ceph-node5 ~]#  sed '/Manager/ d' employee.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 


删除从第一次匹配 Jason 的行至第 4 行:

[root@ceph-node5 ~]# sed '/Jason/,4 d' employee.txt
101,John Doe,CEO
105,Jane Miller,Sales Manager


若是开头的 4 行中,没有匹配 Jason 的行,那么上述命令将删除第 4 行之后匹配 Manager的行

 


删除从第一次匹配 Raj 的行至最后一行:

[root@ceph-node5 ~]# sed '/Raj/,$ d' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager

 


删除第一次匹配 Jason 的行和紧跟着它后面的两行:

[root@ceph-node5 ~]# sed '/Jason/,+2 d' employee.txt
101,John Doe,CEO
105,Jane Miller,Sales Manager

 



经常使用的删除命令示例:
删除全部空行

[root@ceph-node5 ~]# sed '/^$/ d' employee.txt

 


删除全部注释行(假定注释行以#开头)

sed '/^#/' employee.txt

 

注意:若是有多个命令, sed 遇到命令 d 时,会删除匹配到的整行数据,其他的命令将没法操做被删除的行。

 

 

把模式空间内容写到文件中(w 命令)

 

命令 w 能够把当前模式空间的内容保存到文件中。默认状况下模式空间的内容每次都会打印到标准输出,若是要把输出保存到文件同时不显示到屏幕上,还须要使用-n 选项。
下面是几个例子:
employee.txt 的内容保存到文件 output.txt,同时显示在屏幕上

[root@ceph-node5 ~]# sed 'w output.txt' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager


employee.txt 的内容保存到文件 output.txt,但不在屏幕上显示

[root@ceph-node5 ~]#  sed -n 'w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


只保存第 2 行:

[root@ceph-node5 ~]# sed -n '2 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager


保存第 1 至第 4 行:

[root@ceph-node5 ~]# sed -n '1,4 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer


保存第 2 行起至最后一行:

[root@ceph-node5 ~]# sed -n '2,$ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


只保存奇数行:

[root@ceph-node5 ~]# sed -n '1~2 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,John Doe,CEO
103,Raj Reddy,Sysadmin
105,Jane Miller,Sales Manager

 



保存匹配 Jane 的行:

[root@ceph-node5 ~]# sed -n '/Jane/ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
105,Jane Miller,Sales Manager

 



保存第一次匹配 Jason 的行至第 4 行:

[root@ceph-node5 ~]# sed -n '/Jason/,4 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 

注意: 若是开始的 4 行里没有匹配到 Jason,那么该命令只保存第 4 行之后匹配到 Jason

 


保存第一次匹配 Raj 的行至最后一行:

[root@ceph-node5 ~]#  sed -n '/Raj/,$ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


保存匹配 Raj 的行至匹配 Jane 的行:

[root@ceph-node5 ~]# sed -n '/Raj/,/Jane/ w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


保存匹配 Jason 的行以及紧跟在其后面的两行:

[root@ceph-node5 ~]# sed -n '/Jason/,+2 w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 

 

 

3、sed 替换命令

 

 流编辑器中最强大的功能就是替换(substitute),

 

sed 替换命令语法

 

 

sed '[address-range|pattern-range] s/original-string/replacement-string/[substitute-flags]' input file

 

 上面提到的语法为:

 

address-range 或 pattern-range(即地址范围和模式范围)是可选的。 若是没有指定,那么 sed 将在全部行上进行替换。
s 即执行替换命令 substitute
original-string 是被 sed 搜索而后被替换的字符串,它能够是一个正则表达式
replacement-string 替换后的字符串
substitute-flags 是可选的,下面会具体解释

 

请谨记原始输入文件不会被修改, sed 只在模式空间中执行替换命令,而后输出模式空间的内容。
下面是一些简单的替换示例(替换的部分用黑体标明)
Director 替换全部行中的 Manager:

 

[root@ceph-node5 ~]# sed 's/Manager/Director/' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Director
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Director

 


只把包含 Sales 的行中的 Manager 替换为 Director:

[root@ceph-node5 ~]# sed '/Sales/s/Manager/Director/' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Director

 

注意: 本例因为使用了地址范围限制,因此只有一个 Manager 被替换了

 

 

全局标志 g

 

 

 

g 表明全局(global) 默认状况下, sed 至会替换每行中第一次出现的 original-string。若是你要替换每行中出现的全部 original-string,就须要使用 g


用大写 A 替换第一次出现的小写字母 a

[root@ceph-node5 ~]#  sed 's/a/A/' employee.txt
101,John Doe,CEO
102,JAson Smith,IT Manager
103,RAj Reddy,Sysadmin
104,AnAnd Ram,Developer
105,JAne Miller,Sales Manager

 



把全部小写字母 a 替换为大写字母 A

[root@ceph-node5 ~]# sed 's/a/A/g' employee.txt
101,John Doe,CEO
102,JAson Smith,IT MAnAger
103,RAj Reddy,SysAdmin
104,AnAnd RAm,Developer
105,JAne Miller,SAles MAnAger


注意:上述例子会在全部行上替换,由于没有指定地址范围。

 

 数字标志(1,2,3 ….)

 

使用数字能够指定 original-string 出现的次序。 只有第 n 次出现的 original-string 才会触发替换。 每行的数字从 1 开始,最大为 512
好比/11 会替换每行中第 11 次出现的 original-string
下面是几个例子:
把第二次出现的小写字母 a 替换为大写字母 A

[root@ceph-node5 ~]# sed 's/a/A/2' employee.txt
101,John Doe,CEO
102,Jason Smith,IT MAnager
103,Raj Reddy,SysAdmin
104,Anand RAm,Developer
105,Jane Miller,SAles Manager

 

为了方便,请先创建以下文件:

[root@ceph-node5 ~]# vim substitute-locate.txt
locate command is used to locate files
locate command uses database to locate files
locate command can also use regex for searching

 



使用刚才创建的文件,把每行中第二次出现的 locate 替换为 find:

[root@ceph-node5 ~]#  sed 's/locate/find/2' substitute-locate.txt
locate command is used to find files
locate command uses database to find files
locate command can also use regex for searching

 


注意:第 3 行中 locate 只出现了一次,因此没有替换任何内容

 

打印标志 p(print)

 

 

命令 p 表明 print。当替换操做完成后,打印替换后的行。与其余打印命令相似, sed 中比较有用的方法是和-n 一块儿使用以抑制默认的打印操做。
只打印替换后的行:

[root@ceph-node5 ~]# sed -n 's/John/Johnny/p' employee.txt
101,Johnny Doe,CEO

 


在以前的数字标志的例子中,使用/2 来替换第二次出现的 locate。第 3 行中 locate 只出现了一次,因此没有替换任何内容。使用 p 标志能够只打印替换过的两行。


把每行中第二次出现的 locate 替换为 find 并打印出来:

[root@ceph-node5 ~]# sed -n 's/locate/find/2p' substitute-locate.txt
locate command is used to find files
locate command uses database to find files

 

 

写标志 w

标志 w 表明 write。当替换操做执行成功后,它把替换后的结果保存的文件中。 多数人更倾向于使用 p 打印内容,而后重定向到文件中。

为了对 sed 标志有个完整的描述, 在这里把这个标志也提出来了

 

 

只把替换后的内容写到 output.txt :

[root@ceph-node5 ~]# sed -n 's/John/Johnny/w output.txt' employee.txt
[root@ceph-node5 ~]# cat output.txt
101,Johnny Doe,CEO

 


和以前使用的命令 p 同样,使用 w 会把替换后的内容保存到文件 output.txt 中。
把每行第二次出现的 locate 替换为 find,把替换的结果保存到文件中,同时显示输入文件全部内容:

[root@ceph-node5 ~]# sed 's/locate/find/2w output.txt' substitute-locate.txt
locate command is used to find files
locate command uses database to find files
locate command can also use regex for searching
[root@ceph-node5 ~]# cat output.txt
locate command is used to find files
locate command uses database to find files

 

 

忽略大小写标志 i (ignore)

 

替换标志 i 表明忽略大小写。 可使用 i 来以小写字符的模式匹配 original-string。 该标志只有 GNU Sed 中才可以使用。
下面的例子不会把 John 替换为 Johnny,由于 original-string 字符串是小写形式:

[root@ceph-node5 ~]# sed 's/john/Johnny/' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 



john John 替换为 Johnny:

[root@ceph-node5 ~]#  sed 's/john/Johnny/i' employee.txt
101,Johnny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 

 

执行命令标志 e (excuate)


替换标志 e 表明执行(execute)。该标志能够将模式空间中的任何内容当作 shell 命令执行,
并把命令执行的结果返回到模式空间。 该标志只有 GNU Sed 中才可以使用。
下面是几个例子:
为了下面的例子,请先创建以下文件:

[root@ceph-node5 ~]# vim files.txt
/etc/passwd
/etc/group

 



files.txt 文件中的每行前面添加 ls –l 并打击结果:

[root@ceph-node5 ~]#  sed 's/^/ls -l/' files.txt
ls -l/etc/passwd
ls -l/etc/group

 


files.txt 文件中的每行前面添加 ls –l 并把结果做为命令执行:

[root@ceph-node5 ~]#  sed 's/^/ls -l /e' files.txt
-rw-r--r--. 1 root root 890 Oct 29 16:20 /etc/passwd
-rw-r--r--. 1 root root 457 Oct 29 16:20 /etc/group

 



使用替换标志组合


根据须要能够把一个或多个替换标志组合起来使用。
下面的例子将把每行中出现的全部 Manager manager 替换为 Director。而后把替换后的内容打印到屏幕上,同时把这些内容保存到 output.txt 文件中。
使用 g,i,p w 的组合:

[root@ceph-node5 ~]# sed -n 's/manager/Director/igpw output.txt' employee.txt
102,Jason Smith,IT Director
105,Jane Miller,Sales Director
[root@ceph-node5 ~]# cat output.txt
102,Jason Smith,IT Director
105,Jane Miller,Sales Director

 

 

 

 

sed 替换命令分界符


上面全部例子中,咱们都是用了 sed 默认的分界符/,s/original-string/replacement-string/g

若是在 original-string replacement-string 中有/,那么须要使用反斜杠\来转义。为了便于示例,请先创建下面文件:

 

[root@ceph-node5 ~]# vim path.txt
reading /usr/local/bin directory

 


限制使用 sed /usr/local/bin 替换为/usr/bin。 在下面例子中, sed 默认的分界符/都被\转义了:

[root@ceph-node5 ~]# sed 's/\/usr\/local\/bin/\/usr\/bin/' path.txt
reading /usr/bin directory


这很难看。 若是要替换一个很长的路径,每一个/前面都使用\转义,会显得很混乱。幸运的是,你可使用任何一个字符做为 sed 替换命令的分界符,如 | ^ @ 或者 !
下面的全部例子都比较易读。 这里再也不显示下面例子的结果,由于它们的结果和上面的例子是相同的。 我我的比较倾向于使用@或者!来替换路径,但使用什么看你本身的偏好了。

sed 's|/usr/local/bin|/usr/bin|' path.txt
sed 's^/usr/local/bin^/usr/bin^' path.txt
sed 's@/usr/local/bin@/usr/bin@' path.txt
sed 's!/usr/local/bin!/usr/bin!' path.txt

 

 

 

单行内容上执行多个命令


一如以前所说, sed 执行的过程是读取内容、执行命令、打印结果、重复循环。 其中执行命令部分,能够由多个命令执行, sed 将一个一个地依次执行它们。
例如,你有两个命令, sed 将在模式空间中执行第一个命令,而后执行第二个命令。

若是第一个命令改变了模式空间的内容,第二个命令会在改变后的模式空间上执行(此时模式空间的内容已经不是最开始读取进来的内容了)
下面的例子演示了在模式空间内执行两个替换命令的过程:
Developer 替换为 IT Manager,而后把 Manager 替换为 Director:

[root@ceph-node5 ~]# sed '{
> s/Developer/IT Manager/
> s/Manager/Director/
>  }' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Director
103,Raj Reddy,Sysadmin
104,Anand Ram,IT Director
105,Jane Miller,Sales Director

 


咱们来分析下第 4 行的执行过程:

1.读取数据: 在这一步, sed 读取内容到模式空间,此时模式空间的内容为:104,Anand Ram,Developer
2.执行命令: 第一个命令, s/Developer/IT Manager/执行后,模式空间的内容为:104,Anand Ram,IT Manager
如今在模式空间上执行第二个命令 s/Manager/Director/,执行后,模式空间内容为:104,Anand Ram,IT Director
谨记: sed 在第一个命令执行的结果上,执行第二个命令。
3.打印内容:打印当前模式空间的内容,以下:104,Anand Ram,IT Director
4.重复循环: 移动的输入文件的下一行,而后重复执行第一步,即读取数据 

 

 

&的做用——获取匹配到的模式


当在 replacement-string 中使用&时,它会被替换成匹配到的 original-string 或正则表达式,这是个颇有用的东西。
看下面示例:
给雇员 ID(即第一列的 3 个数字)加上[ ],101 改为[101]

[root@ceph-node5 ~]# sed 's/^[0-9][0-9][0-9]/[&]/g' employee.txt
[101],John Doe,CEO
[102],Jason Smith,IT Manager
[103],Raj Reddy,Sysadmin
[104],Anand Ram,Developer
[105],Jane Miller,Sales Manager

 



把每一行放进< >中:

[root@ceph-node5 ~]# sed 's/^.*/<&>/' employee.txt
<101,John Doe,CEO>
<102,Jason Smith,IT Manager>
<103,Raj Reddy,Sysadmin>
<104,Anand Ram,Developer>
<105,Jane Miller,Sales Manager>

 

 

分组替换(单个分组)


跟在正则表达式中同样, sed 中也可使用分组。分组以\(开始,以\)结束。 分组能够用在回溯引用中。
回溯引用即从新使用分组所选择的部分正则表达式, 在 sed 替换命令的 replacement-string

中和正则表达式中,均可以使用回溯引用。
单个分组:

[root@ceph-node5 ~]# sed 's/\([^,]*\).*/\1/g' employee.txt
101
102
103
104
105

 



上面例子中:

正则表达式\([^,]*\)匹配字符串从开头到第一个逗号之间的全部字符(并将其放入第一个分组中)
replacement-string 中的\1 将替代匹配到的分组
g 便是全局标志

 

下面这个例子只会显示/etc/passwd 的第一列,即用户名:

[root@ceph-node5 ~]#  sed 's/\([^:]*\).*/\1/' /etc/passwd

 


下面的例子,若是单词第一个字符为大写,那么会给这个大写字符加上()

[root@ceph-node5 ~]# echo "The Geek Stuff"|sed 's/\(\b[A-Z]\)/\(\1\)/g'
(T)he (G)eek (S)tuff

 


请先创建下面文件,以便下面的示例使用:

[root@ceph-node5 ~]# vim numbers.txt
1
12
123
1234
12345
123456

 



格式化数字,增长其可读性:

[root@ceph-node5 ~]# sed 's/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g' numbers.txt
1
12
123
1,234
12,345
123,456

 

 

分组替换(多个分组)


你可使用多个\(\)划分多个分组,使用多个分组时,须要在 replacement-string 中使用\n来指定第 n 个分组。以下面的示例。
只打印第一列(雇员 ID)和第三列(雇员职位):

[root@ceph-node5 ~]#  sed 's/^\([^,]*\),\([^,]*\),\([^,]*\)/\1,\3/' employee.txt
101,CEO
102,IT Manager
103,Sysadmin
104,Developer
105,Sales Manager


在这个例子中,能够看到, original-string 中,划分了 3 个分组,以逗号分隔。

([^,]*\) 第一个分组,匹配雇员 ID,为字段分隔符
([^,]*\) 第二个分组,匹配雇员姓名,为字段分隔符
([^,]*\) 第三个分组,匹配雇员职位,为字段分隔符,
上面的例子演示了如何使用分组 \
1 表明第一个分组(雇员 ID),出如今第一个分组以后的逗号 \3 表明第二个分组(雇员职位)

 

注意: sed 最多能处理 9 个分组,分别用\1 \9 表示。

交换第一列(雇员 ID)和第二列(雇员姓名)

[root@ceph-node5 ~]# sed 's/^\([^,]*\),\([^,]*\),\([^,]*\)/\2,\1,\3/' employee.txt
John Doe,101,CEO
Jason Smith,102,IT Manager
Raj Reddy,103,Sysadmin
Anand Ram,104,Developer
Jane Miller,105,Sales Manager

 

 

 

 

GNU Sed 专有的替换标志

 

 


下面的标志,只有 GNU 版的 sed 才能使用。 它们能够用在替换命令中的 replacement-string里面.

 

 

\l 标志

 

 


当在 replacement-string 中使用\l 标志时,它会把紧跟在其后面的字符当作小写字符来处理。
如你所知,下面的例子将把 John 换成 JOHNNY:

[root@ceph-node5 ~]# sed 's/John/JOHNNY/' employee.txt  
101,JOHNNY Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


下面的例子,在 replacement-string 中的 H 前面放置了\l 标志,它会把 JOHNNY 中的 H 换成小写的 h:

[root@ceph-node5 ~]# sed -n 's/John/JO\lHNNY/p' employee.txt
101,JOhNNY Doe,CEO

 

 

\L 标志

 

 

 


当在 replacement-string 中使用\l 标志时,它会把后面全部的字符都当作小写字符来处理。
下面的例子,在 replacement-string 中的 H 前面放置了\L 标志,它会把 H 和它后面的全部字符都换成小写:

[root@ceph-node5 ~]#  sed -n 's/John/JO\LHNNY/p' employee.txt
101,JOhnny Doe,CEO

 

\u 标志


\l 相似,只不过是把字符换成大写。 当在 replacement-string 中使用\u 标志时,它会把紧跟在其后面的字符当作大写字符来处理。

下面的例子中, replacement-string 里面的 h 前面有\u 标志,因此 h 将被换成大写的 H:

[root@ceph-node5 ~]#  sed -n 's/John/jo\uhnny/p' employee.txt
101,joHnny Doe,CEO

 

 

 

 

 

 

\U 标志

 


当在 replacement-string 中使用\U 标志时,它会把后面全部的字符都当作大写字符来处理。
下面的例子中, replacement-string 里面的 h 前面有\U 标志,因此 h 及其之后的全部字符,都将被换成大写:

[root@ceph-node5 ~]# sed -n 's/John/jo\Uhnny/p' employee.txt
101,joHNNY Doe,CEO

 

 

 

\E 标志


\E 标志须要和\U \L 一块儿使用,它将关闭\U \L 的功能。下面的例子将把字符串"Johnny Boy"的每一个字符都以大写的形式打印出来,

由于在 replacement-string 前面使用了\U 标志:

[root@ceph-node5 ~]# sed -n 's/John/\UJohnny Boy/p' employee.txt
101,JOHNNY BOY Doe,CEO

 


下面将把 John 换成 JOHNNY Boy:

[root@ceph-node5 ~]# sed -n 's/John/\UJohnny\E Boy/p' employee.txt
101,JOHNNY Boy Doe,CEO

 

这个例子只把 Johnny 显示为大写,由于在 Johnny 后面使用了\E 标志(关闭了\U 的功能)替换标志的用法

上面的例子仅仅展现了这些标志的用法和功能。 然而,若是你使用的是具体的字符串,那么这些选项未必有什么做用,由于你能够在须要的地方写出精确的字符串,

而不须要使用这些标志进行转换。和分组配合使用时,这些选项就显得颇有用了。

前面例子中咱们已经学会了如何使用分组调换第一列和第三列的位置。 使用上述标志,能够把整个分组转换为小写或大写。
下面的例子,雇员 ID 都显示为大写,职位都显示为小写:

[root@ceph-node5 ~]# sed 's/\([^,]*\),\([^,]*\),\([^,]*\)/\U\2\E,\1,\L\3/' employee.txt
JOHN DOE,101,ceo
JASON SMITH,102,it manager
RAJ REDDY,103,sysadmin
ANAND RAM,104,developer
JANE MILLER,105,sales manager

 


这个例子中:

\U\2\E 把第二个分组转换为大写,而后用\E 关闭转换
\L\3 把第三个分组转换为小写

 

 

 

 

 

 

 

4、简单正则表达式


从以上案例中咱们不难发现,咱们编写的脚本指令须要指定一个地址来决定操做范围,若是不指定则默认对文件的全部行操做。

 

如:sed   'd'     test.txt              将删除 test.txt 的全部行,而'2d'
则仅删除第二行。
1.为 sed 指令肯定操做地址:
number              指定输入文件的惟一行号。
first~step       以 first 开始,并指定操做步长为 step,如 1~2,指定第一行,
第三行,第五行... 为操做地址。
[jacob@localhost ~] #sed   -n   '1~2p'   test.txt              打印文件的奇数行。
DEVICE=eth0
BOOTPROTO=static
NETMASK=255.255.255.0
2~5,则能够指定第二行开始,每 5 行匹配一次操做地址。
$                     匹配文件的最后一行。
/regexp/         //中间包含的是正则表达式,经过正则表达式匹配操做地址。
注: //空的正则表达式,匹配最近一次正则表达式的匹配地址,会在后面使用看出效果。
\cregexpc         匹配扩展正则表达式,c 字符可使用任意字符替代。
addr1,addr2       匹配从操做地址 1 到操做地址 2 的全部行。
[jacob@localhost ~] #sed   '2,8d'   test.txt              #删除 28 中间的全部行。
DEVICE=eth0
addr1,+N              匹配地址 1 以及后面的 N 行内容。

 



2.正则表达式概述(对你要找内容的一种描述)

char                     字符自己就匹配字符自己,如/abc/就是定位包含 abc的行。
*                     匹配前面表达式出现了 0 或若干次,如/a*/能够帮你找到a,aa,aaa,... ...等等。
\+                     相似于*,但匹配前面表达式的 1 次或屡次,这属于扩展正则表达式。
\?                     相似于*,但匹配前面表达式的 0 次或 1 次,这属于扩展正则表达式。
\{i\}                相似于*,但匹配前面表达式的 i 次(i 为整数),如: a\{3\}能够帮你找到 aaa。
\{i,j\}            匹配前面表达式的 i 到 j 次,如 a\{1,2\}能够帮你找到 a 或aa 或 aaa。
\{i,\}              匹配前面表达式至少 i 次。
\( \)                将\( \)内的模式存储在保留空间。最多能够存储 9 个独立子模式,
                     可经过转义\1 至\9 重复保留空间的内容至此点。
\n                     转义\1 至\9 重复保留空间的内容至此点。
例:test.txt 的内容为 ssttss
grep '\(ss\)tt\1' test.txt                     \1 表示将 ss 重复在 tt 后面
该 grep 命令等同于grep   ssttss   test.txt       在 test.txt 文件中找 ssttss
.                     (点)匹配任意字符。
^                     匹配行的开始,如^test       将匹配全部以 test 开始的行。
$                     匹配行的结尾,如 test$       将匹配全部以 test 结尾的行。
[]                   匹配括号中的任意单个字符,如 a[nt]       将匹配 an 或at。
[^]                 匹配不包含在[]中的字符,如[^a-z]       将匹配除 a-z之外的字符。
\n                   匹配换行符。
\char              转义特殊字符,如\*,就是匹配字面意义上的星号。

 

 

行的开头 ( ^ )

^ 匹配每一行的开头


显示以 103 开头的行:

[root@ceph-node5 ~]# sed -n '/^103/ p' employee.txt
103,Raj Reddy,Sysadmin

 


只有^出如今正则表达式开头时,它才匹配行的开头。 因此, ^N 匹配全部以 N 开头的行。

 


行的结尾
( $ )


$匹配行的结尾。


显示以字符
r 结尾的行:

[root@ceph-node5 ~]# sed -n '/r$/ p' employee.txt
102,Jason Smith,IT Manager
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 单个字符 ( . )

元字符点 . 匹配除换行符以外的任意单个字符

.匹配单个字符
.. 匹配两个字符
...匹配三个字符
....以此类推

 

 

匹配 0 次或屡次 ( * )


星号*匹配 0 个或多个其前面的字符。如: 1* 匹配 0 个或多个 1
先创建下面文件:

[root@ceph-node5 ~]# cat log.txt
log: input.txt
log:
log: testing resumed
log:
log:output created

 


假设你想查看那些包含 log 而且后面有信息的行, log 和信息之间可能有 0 个或多个空格,同时不想查看那些 log:后面没有任何信息的行。
显示包含
log:而且 log 后面有信息的行, log 和信息之间可能有空格:

[root@ceph-node5 ~]#  sed -n '/log: *./p' log.txt
log: input.txt
log: testing resumed
log:output created

 

注意: 上面例子中,后面的点.是必需的,若是没有, sed 只会打印全部包含 log 的行。


匹配一次或屡次
( \+ )


“\+”匹配一次或屡次它前面的字符,例如 空格\+ “ \+”匹配至少一个或多个空格。
仍旧使用
log.txt 这个文件来做示例。
显示包含
log:而且 log:后面有一个或多个空格的全部行:

[root@ceph-node5 ~]# sed -n '/log: \+/ p' log.txt
log: input.txt
log: testing resumed

 



注意:这个例子既没有匹配只包含 log:的行,也没有匹配 log:output craeted 这一行,由于 log:后面没有空格。


零次或一次匹配
( \? )


\?匹配 0 次或一次它前面的字符。 如:

[root@ceph-node5 ~]# sed -n '/log: \+/ p' log.txt
log: input.txt
log: testing resumed
[root@ceph-node5 ~]# sed -n '/log: \?/ p' log.txt
log: input.txt
log:
log: testing resumed
log:
log:output created

 

 

转义字符 ( \ )

 

 

若是要在正则表达式中搜寻特殊字符(:*,.),必需使用\来转义它们。

sed -n '/127\.0\.0\.1/ p' /etc/hosts
127.0.0.1 localhost

 

字符集 ( [0-9] )

 

 

字符集匹配方括号中出现的任意一个字符。
匹配包含
23 或者 4 的行:

[root@ceph-node5 ~]# sed -n '/[234]/ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 


在方括号中,可使用链接符-指定一个字符范围。如[0123456789]能够用[0-9]表示,字母能够用[a-z],[A-Z]表示,等等。
匹配包含
23 或者 4 的行(另外一种方式):

[root@ceph-node5 ~]# sed -n '/[2-4]/ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer

 

 或操做符 ( | )


管道符号|用来匹配两边任意一个子表达式。 子表达式 1|子表达式 2 匹配子表达式 1 或者子表达式 2
打印包含 101 或者包含 102 的行:

[root@ceph-node5 ~]# sed -n '/101\|102/ p' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager

 


须要注意, | 须要用\转义。
打印包含数字 2~3 或者包含 105 的行:

[root@ceph-node5 ~]# sed -n '/[2-3]\|105/ p' employee.txt
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
105,Jane Miller,Sales Manager

 

 

精确匹配 m ( {m} )




正则表达式后面跟上{m}标明精确匹配该正则 m 次。
请先创建以下文件:

[root@ceph-node5 ~]# vim numbers.txt
1
12
123
1234
12345
123456

 


打印包含任意数字的行(这个命令将打印全部行):

[root@ceph-node5 ~]# sed -n '/[0-9]/ p' numbers.txt
1
12
123
1234
12345
123456

 


打印包含 5 个数字的行:

[root@ceph-node5 ~]# sed -n '/^[0-9]\{5\}$/ p' numbers.txt
12345

 


注意这里必定要有开头和结尾符号,即^$,而且{}都要用\转义

 

匹配 m n ( {m,n} ):


正则表达式后面跟上{m,n}代表精确匹配该正则至少 m,最多 n 次。 m n 不能是负数,而且要小于 255.
打印由 3 5 个数字组成的行:

[root@ceph-node5 ~]# sed -n '/^[0-9]\{3,5\}$/ p' numbers.txt
123
1234
12345

 



正则表达式后面跟上{m,}代表精确匹配该正则至少 m,最多不限。 (一样,若是是{,n}代表最多匹配 n 次,最少一次)

字符边界 ( \b )

\b 用来匹配单词开头(\bxx)或结尾(xx\b)的任意字符,所以\bthe\b 将匹配 the,但不匹配 they。\bthe 将匹配 the they.
请先创建以下文件:

[root@ceph-node5 ~]# vim words.txt
word matching using: the
word matching using: thethe
word matching using: they

 

 

匹配包含 the 做为整个单词的行:

[root@ceph-node5 ~]# sed -n '/\bthe\b/ p' words.txt
word matching using: the

 


注意:若是没有后面那个\b,将匹配全部行。
匹配全部以 the 开头的单词:

[root@ceph-node5 ~]# sed -n '/\bthe/ p' words.txt
word matching using: the
word matching using: thethe
word matching using: they

 

回溯引用 ( \n )

使用回溯引用,能够给正则表达式分组,以便在后面引用它们。
只匹配重复 the 两次的行:

[root@ceph-node5 ~]# sed -n '/\(the\)\1/ p' words.txt
word matching using: thethe

 


同理, ”\([0-9]\)\1” 匹配连续两个相同的数字,如 11,22,33 …..

 

sed 替换中使用正则表达式

 

下面是一些使用正则表达式进行替换的例子。
employee.txt 中每行最后两个字符替换为”,Not Defined”:

[root@ceph-node5 ~]# sed -n 's/..$/,Not Defined/ p' employee.txt
101,John Doe,C,Not Defined
102,Jason Smith,IT Manag,Not Defined
103,Raj Reddy,Sysadm,Not Defined
104,Anand Ram,Develop,Not Defined
105,Jane Miller,Sales Manag,Not Defined

 


删除以 Manager 开头的行的后面的全部内容:

[root@ceph-node5 ~]# sed 's/^Manager.*//' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


这个示例好像有点问题啊,我以为应该是 sed ‘s/^Manager.*/Manager/’
删除全部以#开头的行:

[root@ceph-node5 ~]#  sed -e 's/#.*// ; /^$/ d' employee.txt 
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager


我以为用 sed ‘/^#/ d’ 更好
创建下面的 test.html 文件:

[root@ceph-node5 ~]# vim test.html
<html><body><h1>Hello World!</h1></body></html>

 


清除 test.html 文件中的全部 HTML 标签:

[root@ceph-node5 ~]# sed 's/<[^>]*>//g' test.html
Hello World!

 


删除全部注释行和空行:

[root@ceph-node5 ~]# sed -e 's/#.*// ; /^$/ d' /etc/profile

 


只删除注释行,保留空行:

[root@ceph-node5 ~]# sed '/#.*/ d' /etc/profile 

 

使用 sed 能够把 DOS 的换行符(CR/LF)替换为 Unix 格式。 当把 DOS 格式的文件拷到 Unix 上,你会发现,每行结尾都有\r\n .
使用 sed DOS 格式的文件转换为 Unix 格式:

sed ‘s/.$//’ filename 

 

 

 

5、执行 sed

 

单行内执行多个 sed 命令

 

 

 

1. 使用多命令选项 –e
多命令选项-e 使用方法以下:

sed –e 'command1' –e 'command2' –e 'command3'

 


/etc/passwd 文件中,搜索 rootnobody mail:

[root@ceph-node5 ~]# sed -n -e '/^root/ p' -e '/^nobody/ p' -e '/^mail/ p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 




2. 使用\ 折行执行多个命令


在执行很长的命令,好比使用
-e 选项执行多个 sed 命令时,可使用\来把命令折到多行

[root@ceph-node5 ~]# sed -n -e '/^root/ p' \
> -e '/^nobody/ p' \
> -e '/^mail/ p' \
> /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 


3. 使用{ }把多个命令组合
若是要执行不少
sed 命令,可使用{ }把他们组合起来执行,如:

[root@ceph-node5 ~]# sed -n '{
> /^root/ p
> /^nobody/ p
> /^mail/ p
> }' /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 

 

 

sed 脚本文件

若是用重复使用一组 sed 命令,那么能够创建 sed 脚本文件,里面包含全部要执行的 sed 命令,而后用-f 选项来使用。
首先创建下面文件,里面包含了全部要执行的
sed 命令。 前面已经解释过各个命令的含义,
如今你应该知道全部命令的意思了。

[root@ceph-node5 ~]# vim mycommands.sed
s/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1, \3/g
s/^.*/<&>/
s/Developer/IT Manager/
s/Manager/Director/

 

如今执行脚本里面的命令:

[root@ceph-node5 ~]# sed -f mycommands.sed employee.txt
<John Doe,101, CEO>
<Jason Smith,102, IT Director>
<Raj Reddy,103, Sysadmin>
<Anand Ram,104, IT Director>
<Jane Miller,105, Sales Director>

 

sed 注释

 

sed 注释以#开头。由于 sed 是比较晦涩难懂的语言,因此你如今写下的 sed 命令,时间一长,再看时就不那么容易理解了。

所以,建议把写脚本时的初衷做为注释,写到脚本里面。 以下所示:

[root@ceph-node5 ~]#  vim mycommands.sed
#交换第一列和第二列 s/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1, \3/g #把整行内容放入<>中 s/^.*/<&>/ #把 Developer 替换为 IT Manager s/Developer/IT Manager/ #把 Manager 替换为 Director s/Manager/Director/

 

 


注意: 若是 sed 脚本第一行开始的两个字符是#n 的话, sed 会自动使用-n 选项(即不自动打印模式空间的内容)

 

 

sed 当作命令解释器使用

 


一如你能够把命令放进一个 shell 脚本中,而后调用脚本名称来执行它们同样,你也能够把sed 用做命令解释器。

要实现这个功能,须要在 sed 脚本最开始加入"#!/bin/sed –f",以下所示:

[root@ceph-node5 ~]#  vim myscript.sed
#!/bin/sed -f
#交换第一列和第二列
s/\([^,]*\),\([^,]*\),\(.*\).*/\2,\1, \3/g
#把整行内容放入<>中
s/^.*/<&>/
#把 Developer 替换为 IT Manager
s/Developer/IT Manager/
#把 Manager 替换为 Director
s/Manager/Director/

 

如今,给这个脚本加上可执行权限,而后直接在命令行调用它:

[root@ceph-node5 ~]#  chmod u+x myscript.sed
[root@ceph-node5 ~]#  ./myscript.sed employee.txt
<John Doe,101, CEO>
<Jason Smith,102, IT Director>
<Raj Reddy,103, Sysadmin>
<Anand Ram,104, IT Director>
<Jane Miller,105, Sales Director>

 


也能够指定-n 选项来屏蔽默认输出:

[root@ceph-node5 ~]# vim testscript.sed
#!/bin/sed -nf
/root/ p
/nobody/ p
/mail/ p

 



而后加上可执行权限,执行:

[root@ceph-node5 ~]# chmod u+x testscript.sed
[root@ceph-node5 ~]# ./testscript.sed /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

 


处于测试目的,把 testscript.sed 里面的-n 去掉,而后再执行一次, 观察它是如何运行的。
重要提示
:使用-n 时,必须是-nf.若是你写成-fn,执行脚本时就会得到下面的错误:

[root@ceph-node5 ~]#  ./testscript.sed /etc/passwd
/bin/sed: couldn't open file n: No such file or directory

 

 

 

直接修改输入文件

目前为止,你知道 sed 默认不会修改输入文件, 它只会把输出打印到标准输出上。 当想保存结果时,把输出重定向到文件中(或使用 w 命令)
执行下面的例子以前,先备份一下
employee.txt 文件:

[root@ceph-node5 ~]# cp employee.txt{,.orig}

 


为了修改输入文件,一般方法是把输出重定向到一个临时文件,而后重命名该临时文件:

[root@ceph-node5 ~]# sed 's/John/Johnny/' employee.txt > new-employee.txt  
[root@ceph-node5 ~]# mv new-employee.txt employee.txt

 


相比这种传统方法,能够在 sed 命令中使用-i 选项,使 sed 能够直接修改输入文件:
在原始文件 employee.txt 中,把 John 替换为 Johnny:

[root@ceph-node5 ~]# sed -i 's/John/Johnny/' employee.txt
[root@ceph-node5 ~]# cat employee.txt
101,Johnnyny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


再次提醒: -i 会修改输入文件。 或许这正是你想要的,可是务必当心。 一个保护性的措施是,在-i 后面加上备份扩展,这一 sed 就会在修改原始文件以前,备份一份。
在原始文件
employee.txt 中,把 John 替换为 Johnny,但在替换前备份 employee.txt:

[root@ceph-node5 ~]# sed -ibak 's/John/Johnny/' employee.txt

 


备份的文件以下:

[root@ceph-node5 ~]# cat employee.txtbak
101,Johnnyny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


修改后的原始文件为:

[root@ceph-node5 ~]# cat employee.txt
101,Johnnynyny Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


除了使用-i,也可使用完整样式 –in-place.下面两个命令是等价的:

[root@ceph-node5 ~]# sed -ibak 's/John/Johnny/' employee.txt 
[root@ceph-node5 ~]# sed -in-place=bak 's/John/Johnny/' employee.txt

 



最后,为了继续下面的例子,把原来的 employee.txt 还原回去:

cp employee.txt.orig employee.txt

 



6、sed 附加命令

 

追加命令(命令 a)

 



使用命令 a 能够在指定位置的后面插入新行。
语法:

sed '[address] a the-line-to-append' input-file

 


在第 2 行后面追加一行(原文这里可能有问题,没有写明行号):

[root@ceph-node5 ~]# sed '2 a 203,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
203,Jack Johnson,Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


employee.txt 文件结尾追加一行:

[root@ceph-node5 ~]# sed '$ a 106,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
106,Jack Johnson,Engineer

 



在匹配
Jason 的行的后面追加两行:

[root@ceph-node5 ~]# sed '/Jason/a\
> 203,Jack Johnson,Engineer\
> 204,Mark Smith,Sales Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
203,Jack Johnson,Engineer
204,Mark Smith,Sales Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


追加多行之间能够用\n 来换行,这样就不用折行了,上面的命令等价于:
sed '/Jason/a 203,Jack Johnson,Engineer\n204,Mark Smith,Sales Engineer' employee.txt

 

 

插入命令(命令 i)


插入命令 insert 命令和追加命令相似,只不过是在指定位置以前插入行。
语法:

 sed '[address] i the-line-to-insert' input-file

 


employee.txt 的第 2 行以前插入一行:

[root@ceph-node5 ~]# sed '2 i 203,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
203,Jack Johnson,Engineer
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


employee.txt 最后一行以前,插入一行:

[root@ceph-node5 ~]# sed '$ i 108,Jack Johnson,Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
108,Jack Johnson,Engineer
105,Jane Miller,Sales Manager

 


sed
也能够插入多行。
在匹配
Jason 的行的前面插入两行:

sed '/Jason/i\
203,Jack Johnson,Engineer\
204,Mark Smith,Sales Engineer' employee.txt
101,John Doe,CEO
203,Jack Johnson,Engineer
204,Mark Smith,Sales Engineer
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


修改命令(命令 c)
修改命令 change 能够用新行取代旧行。
语法:

sed '[address] c the-line-to-insert' input-file

 



用新数据取代第 2 :

[root@ceph-node5 ~]# sed '2 c 202,Jack,Johnson,Engineer' employee.txt
101,John Doe,CEO
202,Jack,Johnson,Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


这里命令 c 等价于替换: $ sed '2s/.*/202,Jack,Johnson,Engineer/' employee.txt
sed 也能够用多行来取代一行。
用两行新数据取代匹配
Raj 的行:

[root@ceph-node5 ~]# sed '/Raj/c\
> 203,Jack Johnson,Engineer\
> 204,Mark Smith,Sales Engineer' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
203,Jack Johnson,Engineer
204,Mark Smith,Sales Engineer
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


命令 a、 i 和 c 组合使用


命令 ai c 能够组合使用。下面的例子将完成三个操做:

a 在"Jason"后面追加"Jack Johnson"
i 在"Jason"前面插入"Mark Smith"
c 用"Joe Mason"替代"Jason"

 


以下:

[root@ceph-node5 ~]# sed '/Jason/ {a\
> 204,Jack Johnson,Engineer
> i\
> 202,Mark Smith,Sales Engineer
> c\
> 203,Joe Mason,Sysadmin
> }' employee.txt
101,John Doe,CEO
202,Mark Smith,Sales Engineer
203,Joe Mason,Sysadmin
204,Jack Johnson,Engineer
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 

打印不可见字符(命令 l)

 


命令 l 能够打印不可见的字符,好比制表符\t,行尾标志$等。
请先创建下面文件以便用于后续测试,请确保字段之间使用制表符
(Tab )分开:

[root@ceph-node5 ~]# vim  tabfile.txt
fname First Name
lname Last Name
mname Middle Name

 

 

使用命令 l,把制表符显示为\t,行尾标志显示为 EOL:

[root@ceph-node5 ~]# sed -n 'l' tabfile.txt
fname First Name$
lname Last Name$
mname Middle Name$

 


若是在 l 后面指定了数字,那么会在第 n 个字符处使用一个不可见自动折行,效果以下:

[root@ceph-node5 ~]# sed -n 'l 20' employee.txt
101,John Doe,CEO$
102,Jason Smith,IT \
Manager$
103,Raj Reddy,Sysad\
min$
104,Anand Ram,Devel\
oper$
105,Jane Miller,Sal\
es Manager$

 


这个功能只有 GNU sed 才有。

 

打印行号(命令=)


命令=会在每一行后面显示该行的行号。

打印全部行号:

[root@ceph-node5 ~]#  sed '=' employee.txt
1
101,John Doe,CEO
2
102,Jason Smith,IT Manager
3
103,Raj Reddy,Sysadmin
4
104,Anand Ram,Developer
5
105,Jane Miller,Sales Manager

 


提示: 把命令=和命令 N 配合使用,能够把行号和内容显示在同一行上(下文解释)
只打印
1,2,3 行的行号:

[root@ceph-node5 ~]# sed '1,3 =' employee.txt
1
101,John Doe,CEO
2
102,Jason Smith,IT Manager
3
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 


打印包含关键字”Jane”的行的行号,同时打印输入文件中的内容:

[root@ceph-node5 ~]#  sed '/Jane/ =' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
5
105,Jane Miller,Sales Manager

 


若是你想只显示行号但不显示行的内容,那么使用-n 选项来配合命令=:

[root@ceph-node5 ~]# sed -n '/Raj/ =' employee.txt
3

 


打印文件的总行数:

[root@ceph-node5 ~]# sed -n '$ =' employee.txt
5

 

 

转换字符(命令 y)

 


命令 y 根据对应位置转换字符。好处之一即是把大写字符转换为小写,反之亦然。
下面例子中,将把
a 换为 Ab 换为 Bc 换为 C,以此类推:

[root@ceph-node5 ~]#  sed 'y/abcde/ABCDE/' employee.txt
101,John DoE,CEO
102,JAson Smith,IT MAnAgEr
103,RAj REDDy,SysADmin
104,AnAnD RAm,DEvElopEr
105,JAnE MillEr,SAlEs MAnAgEr

 


把全部小写字符转换为大写字符:

[root@ceph-node5 ~]# sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' employee.txt
101,JOHN DOE,CEO
102,JASON SMITH,IT MANAGER
103,RAJ REDDY,SYSADMIN
104,ANAND RAM,DEVELOPER
105,JANE MILLER,SALES MANAGER

 

操做多个文件

 


以前的例子都只操做了单个文件, sed 也能够同时处理多个文件。
/etc/passwd 中搜索 root 并打印出来:

[root@ceph-node5 ~]# sed -n '/root/ p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

 


/etc/group 中搜索 root 并打印出来:

[root@ceph-node5 ~]# sed -n '/root/ p' /etc/group
root:x:0:

 


同时在/etc/passwd /etc/group 中搜索 root:

[root@ceph-node5 ~]# sed -n '/root/ p' /etc/passwd /etc/group
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
root:x:0:

 


 退出 sed(命令 q)


命令 q 终止正在执行的命令并退出 sed
以前提到,正常的
sed 执行流程是:读取数据、执行命令、打印结果、重复循环。
sed 遇到 q 命令,便马上退出,当前循环中的后续命令不会被执行,也不会继续循环。
打印第
1 行后退出:

[root@ceph-node5 ~]# sed 'q' employee.txt
101,John Doe,CEO

 


打印第 5 行后退出,即只打印前 5 行:

 

[root@ceph-node5 ~]# sed '5 q' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 



打印全部行,直到遇到包含关键字 Manager 的行:

[root@ceph-node5 ~]# sed '/Manager/ q' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager

 


注意: q 命令不能指定地址范围(或模式范围),只能用于单个地址(或单个模式)

 

 

从文件读取数据(命令 r)

在处理输入文件是,命令 r 会从另一个文件读取内容,并在指定的位置打印出来。

下面的例子将读取 log.txt 的内容,并在打印 employee.txt 最后一行以后,把读取的内容打印出来。事实上它把 employee.txt log.txt 合并而后打印出来。

[root@ceph-node5 ~]# sed '$ r log.txt' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
log: input.txt
log:
log: testing resumed
log:
log:output created

 


也能够给命令 r 指定一个模式。下面的例子将读取 log.txt 的内容,而且在匹配’Raj’的行后面打印出来:

[root@ceph-node5 ~]# sed '/Raj/ r log.txt' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
log: input.txt
log:
log: testing resumed
log:
log:output created
104,Anand Ram,Developer
105,Jane Miller,Sales Manager

 

 

sed 模拟 Unix 命令(cat,grep,read)

 

 以前的例子的完成的功能都很像标准的 Unix 命令。使用 sed 能够模拟不少 Unix 命令。完成它们,以便更好地理解 sed 的工做过程

 

模拟 cat 命令

 

cat employee.txt

 


下面的每一个 sed 命令的输出都和上面的 cat 命令的输出同样:

sed 's/JUNK/&/p' employee.txt 
sed -n 'p' employee.txt
sed 'n' employee.txt
sed 'N' employee.txt

 

 

 

模拟 grep 命令



 

grep Jane employee.txt

 


下面的每一个 sed 命令的输出都和上面的 grep 命令的输出同样:

sed -n 's/Jane/&/ p' employee.txt 
sed -n '/Jane/ p' employee.txt

 



grep –v (打印不匹配的行):

sed -n '/Jane/ !p' employee.txt 

 


注意: 这里不能使用 sed –n 's/Jane/&/ !p' 了。



模拟 head 命令

 

head -10 /etc/passwd

 


下面的每一个 sed 命令的输出都和上面的 head 命令的输出同样:

sed '11,$ d' /etc/passwd 
sed -n '1,10 p' /etc/passwd
sed '10 q' /etc/passwd

 

 

 

sed 命令选项



-n 选项

 


以前已经讨论过屡次,而且在不少例子中也使用到了-n 选项。 该选项屏蔽 sed 的默认输出。
也可使用—quiet,或者—silent 来代替-n,它们的做用是相同的。
下面全部命令都是等价的:

sed -n 'p' employee.txt 
sed --quiet 'p' employee.txt

 

 

-f 选项

 


能够把多个 sed 命令保存在 sed 脚本文件中,而后使用-f 选项来调用,这点以前已经演示过
了。你也可使用--
file 来代替-f
下面的命令是等价的:

sed -n -f test-script.sed /etc/passwd  
sed -n -file=test-script.sed /etc/passwd

 

 

-e 选项

 


该选项执行来自命令行的一个
sed 命令,可使用多个-e 来执行多个命令。也可使用-expression 来代替。
下面全部命令都是等价的
:

sed -n -e '/root/ p' /etc/passwd  
sed -n --expression '/root/ p' /etc/passwd

 



-i 选项

下面全部命令都是等价的:
咱们已经提到, sed 不会修改输入文件,只会把内容打印到标准输出,或则使用 w 命令把内容写到不一样的文件中。 咱们也展现了如何使用-i 选项来直接修改输入文件。
在原始文件
employee.txt 中,用 Johnny 替换 John:

sed -i 's/John/Johnny/' employee.txt 

 


执行和上面相同的命令,但在修改前备份原始文件:

sed -ibak 's/John/Johnny/' employee.txt 

 


也可使用--in-place 来代替-i:
下面的命令是等价的:

 sed -ibak 's/John/Johnny/' employee.txt
sed --in-place=bak 's/John/Johnny/' employee.txt

 



 

-c 选项

该选项应和-i 配合使用。使用-i 时,一般在命令执行完成后, sed 使用临时文件来保持更改后的内容,而后把该临时文件重命名为输入文件。

但这样会改变文件的全部者(奇怪的是个人测试结果是它不会改变文件全部者),配合 c 选项,能够保持文件全部者不变。也可使用--copy 来代替。
下面的命令是等价的
:

sed -ibak -c 's/John/Johnny/' employee.txt 
sed --in-place=bak --copy 's/John/Johnny/' employee.txt

 



-l 选项

 


指定行的长度,须要和
l 命令配合使用(注意选项 l 和命令 l,不要弄混了,上面提到的命令 i
和选项 i 也不要搞错)使用-l 选项即指定行的长度。也可使用--line-length 来代替.
下面的命令是等价的:

sed -n -l 20 'l' employee.txt

 



请注意,下面的例子不使用-l(原文是-n,应该是弄错)选项,一样能够获取相同的输出:

sed -n 'l 20' employee.txt --posix option  

 

 

打印模式空间(命令 n)

 


命令 n 打印当前模式空间的内容,而后从输入文件中读取下一行。 若是在命令执行过程当中遇到 n,那么它会改变正常的执行流程。
打印每一行在模式空间中的内容:

sed n employee.txt

 


若是使用了-n 选项,将没有任何输出:

 sed -n n employee.txt

 


再次提醒:不要把选项-n 和命令 n 弄混了)
前面提到, sed 正常流程是读取数据、执行命令、打印输出、重复循环。
命令
n 能够改变这个流程,它打印当前模式空间的内容,而后清除模式空间,读取下一行进
来,而后继续执行后面的命令。
假定命令
n 先后各有两个其余命令,以下:

sed-command-1
sed-command-2
n
sed-command-3
sed-command-4

 


这种状况下, sed-command-1 sed-command-2 会在当前模式空间中执行,而后遇到 n,它打印当前模式空间的内容,并清空模式空间,读取下一行,

而后把 sed-command-3 sed-command-4 应用于新的模式空间的内容。
提示:上面的例子中能够看到,命令
n 自己没有多大用处,然而当它和保持空间的命令配合使用时,就很强大了,后面会详细解释。

 

7、保持空间和模式空间命令

 

 

SED之因此能以行为单位的编辑或修改文本,其缘由在于它使用了两个空间:一个是活动的模式空间(pattern space),另外一个是起辅助做用的暂存缓冲区(holdingspace)这2个空间的使用。

模式空间:如你所知,模式空间用于 sed 执行的正常流程中。 该空间 sed 内置的一个缓冲区,用来存放、修改从输入文件读取的内容。

 保持空间: 保持空间是另一个缓冲区,用来存放临时数据。 Sed 能够在保持空间和模式空间交换数据,可是不能在保持空间上执行普通的 sed 命令。 咱们已经讨论
过,每次循环读取数据过程当中,模式空间的内容都会被清空,然而保持空间的内容则保持不变,不会在循环中被删除。

 

sed编辑器逐行处理文件,并将输出结果打印到屏幕上。sed命令将当前处理的行读入模式空间(pattern space)进行处理,sed在该行上执行完全部命令后就将处理好的行打印到屏幕上(除非以前的命令删除了该行),sed处理完一行就将其从模式空间中删除,而后将下一行读入模式空间,进行处理、显示。处理完文件的最后一行,sed便结束运行。sed在临时缓冲区(模式空间)对文件进行处理,因此不会修改原文件,除非显示指明-i选项。

模式空间:能够想成工程里面的流水线,数据之间在它上面进行处理,用于处理文本行。

保持空间:能够想象成仓库,咱们在进行数据处理的时候,做为数据的暂存区域,用于保留文本行,是保存已经处理过的输入行,默认有一个空行。

 

与模式空间和暂存空间(hold space)相关的命令:

 

n 输出模式空间行,读取下一行替换当前模式空间的行,执行下一条处理命令而非第一条命令。

N 读入下一行,追加到模式空间行后面,此时模式空间有两行。

h 把模式空间的内容复制到保留空间,覆盖模式

H 把模式空间的内容追加到保留空间,追加模式

g 把保留空间的内容复制到模式空间,覆盖模式

G 把保留空间的内容追加到模式空间,追加模式

x 将暂存空间的内容于模式空间里的当前行互换。

! 对所选行之外的全部行应用命令。

注意:暂存空间里默认存储一个空行。



 

 

 

 

请先创建以下文件,以用于保持空间的示例:

[root@ceph-node5 ~]# vim empnametitle.txt
John
Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager


能够看到,在这个文件中,每一个雇员的名称和职位位于连续的两行内。

 

用保持空间替换模式空间(命令 x)

 

命令 x(Exchange)交换模式空间和保持空间的内容。 该命令自己没有多大用处,但若是和其余命令配合使用,则很是强大了。
假定目前模式空间内容为"
line 1",保持空间内容为"line 2"。那么执行命令 x 后,模式空间的内容变为”line 2”,保持空间的内容变为"line 1"

 

 

把模式空间的内容复制到保持空间(命令 h)

 


命令 h(hold)把模式空间的内容复制到保持空间,和命令 x 不一样,命令 h 不会修改当前模式空间的内容。 执行命令 h 时,当前保持空间的内容会被模式空间的内容覆盖。假定目前模式空间内容为"line 1",保持空间内容为"line 2"。那么执行命令 h 后,模式空间的内容仍然为"line 1",保持空间的内容则变为"line 1"
打印管理者的名称
:

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{x;p}' empnametitle.txt
Jason Smith
Jane Miller

 

上面例子中:

/Manager/!h –若是模式空间内容不包含关键字’Manager’(模式后面的!表示不匹配该模式), 那么复制模式空间内容到保持空间。 (这样一来,保持空间的内容可能会
是雇员名称或职位,而不是’Manager’.)注意, 和前面的例子不一样,这个例子中没有使用命令 n 来获取下一行,而是经过正常的流程来读取后续内容。


/Manager/{x;p} –若是模式空间内容包含关键字’Manager’,那么交换保持空间和模式空间的内容,并打印模式空间的内容。 这和咱们在命令 x 的示例中的用法是相同的

 

你也能够把上面命令保存到 sed 脚本中执行:

[root@ceph-node5 ~]# vi h.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{x;p}

[root@ceph-node5 ~]#  chmod u+x h.sed
[root@ceph-node5 ~]# ./h.sed empnametitle.txt
Jason Smith
Jane Miller

 

 

把模式空间内容追加到保持空间(命令 H)

大写 H 命令表示把模式空间的内容追加到保持空间,追加以前保持空间的内容不会被覆盖;
相反, 它在当前保持空间内容后面加上换行符
\n,而后把模式空间内容追加进来。
假定目前模式空间内容为
”line 1”,保持空间内容为”line 2”。那么执行命令 H 后,模式空间的内容没有改变, 仍然为”line 1”,保持空间的内容则变为”line2\nline 1”
打印管理者的名称和职位
(在不一样的行上):

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{H;x;p}' empnametitle.txt
Jason Smith
IT Manager
Jane Miller
Sales Manager

 


上面例子中:

/Manager/!h –若是模式空间内容不包含关键字’Manager’(模式后面的!表示不匹配该模式),那么复制模式空间内容到保持空间。 (这样一来,保持空间的内容可能会
是雇员名称或职位,而不是’Manager’.)。这和以前使用命令 h 的方法是同样的。


/Manager/{H;x;p} –若是模式空间内容包含关键字’Manager’,那么命令 H 把模式空间的内容(也就是管理者的职位)做为新行追加到保持空间,因此保持空间内容会变 为”雇员名称\n 职位”(职位包含关键字 Manager)。 而后命令 x 交换模式空间和保持空间的内容,随后命令 p 打印模式空间的内容。

 

你也能够把上面命令保存到 sed 脚本中执行:

[root@ceph-node5 ~]# vi H-upper.sed 
#!/bin/sed -nf
/Manager/!h
/Manager/{H;x;p}
[root@ceph-node5 ~]# chmod u+x H-upper.sed
[root@ceph-node5 ~]# ./H-upper.sed empnametitle.txt
Jason Smith
IT Manager
Jane Miller
Sales Manager

 


若是想把雇员名称和职位显示在同一行,以分号分开,那么只需稍微修改一下便可,以下:

[root@ceph-node5 ~]#  sed -n -e '/Manager/!h' -e '/Manager/{H;x;;s/\n/:/p}' empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager

 


这个例子除了在第二个-e 后面的命令中加入了替换命令以外,和前面的例子同样。 Hx p 都完成和以前相同的操做;在交换模式空间和保持空间以后,命令 s 把换行符\n 替换为分号,而后打印出来。
你也能够把上面命令保存到
sed 脚本中执行:

[root@ceph-node5 ~]# vi H1-upper.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{H;x;s/\n/:/;p}
[root@ceph-node5 ~]# chmod u+x H1-upper.sed
[root@ceph-node5 ~]# ./H1-upper.sed empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager

 

 

 

把保持空间内容复制到模式空间(命令 g)

 

命令 g(get)把保持空间的内容复制到模式空间。
这样理解:命令 h 保持(hold)住保持空间(hold space),命令 g 从保持空间获取(get)内容。
假定当前模式空间内容为”line 1”,保持空间内容为”line 2”;执行命令 g 以后,模式空间内容变为”line 2”,保持空间内容仍然为”line 2”

打印管理者的名称:

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{g;p}' empnametitle.txt
Jason Smith
Jane Miller

 




上面例子中:

/Manager/!h –最近几个例子都在用它。若是模式空间内容不包含关键字’Manager’,那么就把他复制到保持空间。
/Manager/{g;p} –把保持空间的内容丢到模式空间中,而后打印出来

 

你也能够把上面命令保存到 sed 脚本中执行:

[root@ceph-node5 ~]# vi g.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{g;p}
[root@ceph-node5 ~]# chmod u+x g.sed
[root@ceph-node5 ~]# ./g.sed empnametitle.txt
Jason Smith
Jane Miller

 

把保持空间追加到模式空间(命令 G)


大写 G 命令把当前保持空间的内容做为新行追加到模式空间中。模式空间的内容不会被覆盖,该命令在模式空间后面加上换行符\n,而后把保持空间内容追加进去。
G g 的用法相似于 H h;小写命令替换原来的内容,大写命令追加原来的内容。
假定当前模式空间内容为
”line 1”,保持空间内容为”line 2”;命令 G 执行后,模式空间内容变为”line 1\nline 2”,同时保持空间内容不变,仍然为”line 2”
以分号分隔,打印管理者的名称和职位
:

[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e '/Manager/{x;G;s/\n/:/;p}' empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager


上面例子中:

 

/Manager/!h –和前面的例子同样,若是模式空间内容不包含关键字’Manager’,那么就把他复制到保持空间。
/Manager/{x;G;s/\n/:/;p} –若是模式空间包含’Manager’,那么:
  x –交换模式空间和保持空间的内容。
  G –把保持空间的内容追加到模式空间。
  s/\n/:/ --在模式空间中,把换行符替换为分号:。
  p 打印模式空间内容
  注意: 若是舍去命令 x,即便用/Manager/{G;s/\n/:/;p},那么结果会由“雇员职位: 雇员名称”变成”雇员名称: 雇员职位

 

也可把上述命令写到 sed 脚本中而后执行:

[root@ceph-node5 ~]# vi G-upper.sed
#!/bin/sed -nf
/Manager/!h
/Manager/{x;G;s/\n/:/;p}
[root@ceph-node5 ~]# ./G-upper.sed empnametitle.txt
Jason Smith:IT Manager
Jane Miller:Sales Manager

 

 

8、sed 多行模式及循环

 
 Sed 默认每次只处理一行数据,除非使用 H,G 或者 N 等命令建立多行模式,每行之间用换行符分开。
本章将解释适用于多行模式的
sed 命令。
提示:在处理多行模式是,请务必牢记
^只匹配该模式的开头,即最开始一行的开头,且$只匹配该模式的结尾,即最后一行的结尾。

读取下一行数据并附加到模式空间(命令 N)

就像大写的命令 H G 同样,只会追加内容而不是替换内容,命令 N 从输入文件中读取下一行并追加到模式空间,而不是替换模式空间。
前面提到过,小写命令
n 打印当前模式空间的内容, 并清空模式空间,从输入文件中读取下一行到模式空间,而后继续执行后面的命令。
大写命令
N,不会打印模式空间内容,也不会清除模式空间内容,而是在当前模式空间内容后加上换行符\n,而且从输入文件中读取下一行数据,

追加到模式空间中,而后继续执行后面的命令。
以分号分隔,打印雇员名称和职位
:

[root@ceph-node5 ~]# sed -e '{N;s/\n/:/}' empnametitle.txt
John:Doe
CEO:Jason Smith
IT Manager:Raj Reddy
Sysadmin:Anand Ram
Developer:Jane Miller
Sales Manager

 



这个例子中:

N 追加换行符\n 到当前模式空间(雇员名称)的最后,而后从输入文件读取下一行数据,追加进来。所以,当前模式空间内容变为”雇员名称\n 雇员职位”。
s/\n/:/ 把换行符\n 替换为分号,把分号做为雇员名称和雇员职位的分隔符

流程以下图所示:

 

 

 

 

 

打印多行模式中的第一行(命令 P)

目前为止,咱们已经学会了三个大写的命令(H,N,G),每一个命令都是追加内容而不是替换内容。
如今咱们来看看大写的
D P,虽然他们的功能和小写的 d p 很是类似,但他们在多行模式中有特殊的功能。
以前说到,小写的命令
p 打印模式空间的内容。大写的 P 也打印模式空间内容,直到它遇到换行符\n

 

 

下面的例子将打印全部管理者的名称:

( cat << EOF
John
Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager
EOF
) > empname.txt
[root@ceph-node5 ~]# sed -n -e '/Manager/!h' -e'/Manager/{x;p}' empname.txt
Jason Smith
Jane Miller

 

 

 

 

 

 

删除多行模式中的第一行(命令 D)

以前提到,小写命令 d 会删除模式空间内容,而后读取下一条记录到模式空间,并忽略后面的命令,从头开始下一次循环。
大写命令
D,既不会读取下一条记录,也不会彻底清空模式空间(除非模式空间内只有一行)
它只会:

删除模式空间的部份内容,直到遇到换行符\n
忽略后续命令,在当前模式空间中从头开始执行命令

 

 

 

假设有下面文件,每一个雇员的职位都用@包含起来做为注释。 须要注意的是,有些注释是跨
行的。 如
@Information Technology officer@就跨了两行。请先创建下面示例文件:

[root@ceph-node5 ~]# vim empnametitle-with-commnet.txt
John Doe
CEO @Chief Executive Officer@
Jason Smith
IT Manager @Infromation Technology
Officer@
Raj Reddy
Sysadmin @System Administrator@
Anand Ram
Developer @Senior
Programmer@
Jane Miller
Sales Manager @Sales
Manager@

 


如今咱们的目标是,去掉文件里的注释:

[root@ceph-node5 ~]#  sed -e '/@/{N;/@.*@/{s/@.*@//;P;D}}' empnametitle-with-commnet.txt
John Doe
CEO 
Jason Smith
IT Manager 
Raj Reddy
Sysadmin 
Anand Ram
Developer 
Jane Miller
Sales Manager 

 

也可把上述命令写到 sed 脚本中而后执行:

[root@ceph-node5 ~]# vim D-upper.sed
#!/bin/sed -f
/@/{
N
/@.*@/{s/@.*@//;P;D}
}
[root@ceph-node5 ~]# ./D-upper.sed empnametitle-with-commnet.txt
John Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager

 


这个例子中:

 

/@/{ 这是外传循环。 Sed 搜索包含@符号的任意行,若是找到,就执行后面的命令;若是没有找到,则读取下一行。 为了便于说明,以第 4 行,即”@Information
Technology”(这条注释跨了两行)为例,它包含一个@符合,因此后面的命令会被执行。
N 从输入文件读取下一行,并追加到模式空间,以上面提到的那行数据为例,这里 N 会读取第 5 行,即”Officer@”并追加到模式空间,所以模式空间内容变
为”@Informatioin Technology\nOfficer@”。
/@.*@/ 在模式空间中搜索匹配/@.*@/的模式,即以@开头和结尾的任何内容。当前模式空间的内容匹配这个模式,所以将继续执行后面的命令。
s/@.*@//;P;D 这个替换命令把整个@Information Technology\nOfficer@”替换为空(至关于删除)。 P 打印模式空间中的第一行,而后 D 删除模式空间中的第一行,然
后从头开始执行命令(即不读取下一条记录,又返回到/@/处执行命令)

 

循环和分支(命令 b :label 标签)

使用标签和分支命令 b,能够改变 sed 的执行流程:

:label 定义一个标签
b lable 执行该标签后面的命令。 Sed 会跳转到该标签,而后执行后面的命令。
注意:命令 b 后面能够不跟任何标签,这种状况下,它会直接跳到 sed 脚本的结尾

下面例子将把 empnametitle.txt 文件中的雇员名称和职位合并到一行内,字段之间以分号:
分隔,而且在管理者的名称前面加上一个星号
*

[root@ceph-node5 ~]# vim label.sed
#!/bin/sed -nf
h;n;H;x
s/\n/:/
/Manager/!b end
s/^/*/
:end
p

 


这个脚本中,鉴于以前的例子,你已经知道 h;n;H;x s/\n/:/的做用了。下面是关于分支的操做

Manager/!b end 若是行内不包含关键字”Manager”,则 跳转到’end’标签,请注意,
你能够任意设置你想要的标签名称。 所以,只有匹配 Manager 的雇员名称签名,才会执行 s/^/*/(在行首加上星号*)。
:end 便是标签

 

给这个脚本加上可执行权限,而后执行:

$ chmod u+x label.sed
$ ./label.sed empnametitle.txt
John Doe:CEO
*Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
*Jane Miller:Sales Manager

 


我的以为脚本里面的 h;n;H;x 能够用一个 N 替代, 这样就不用使用保持空间了。
若是不使用标签,还能够: sed 'N;s/\n/:/;/Manager/s/^/\.*/' empnametitle.txt

 

注意:我这里显示的结果:

[root@ceph-node5 ~]#  chmod u+x label.sed
[root@ceph-node5 ~]# ./label.sed empnametitle.txt
John:Doe
CEO:Jason Smith
*IT Manager:Raj Reddy
Sysadmin:Anand Ram
Developer:Jane Miller

 

 

使用命令 t 进行循环


命令 t 的做用是,若是前面的命令执行成功,那么就跳转到 t 指定的标签处,继续往下执行后续命令。 不然,仍然继续正常的执行流程。
下面例子将把 empnametitle.txt 文件中的雇员名称和职位合并到一行内,字段之间以分号:分隔,而且在管理者的名称前面加上三个星号*
提示:咱们只需把前面例子中的替换命令改成 s/^/***/便可带到该目的,下面这个例子仅仅是为了解释命令 t 是如何运行的。

$ vi lable-t.sed
#!/bin/sed -nf
h;n;H;x
s/\n/:/
: repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
p
$ chmod u+x lable-t.sed
$ ./lable-t.sed empnametitle.txt
John Doe:CEO
***Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
***Jane Miller:Sales Manager
这个例子中:

 

这个例子中:

下面的代码执行循环
:repeat
/Manager/s/^/*/
/\*\*\*/! t repeat
/Manager/s/^/*/ 若是匹配到 Manager,在行首加上星号*
/\*\*\*/!t repeat 若是没有匹配到三个连续的星号*(用/\*\*\*/!来表示),而且前面
一行的替换命令成功执行了,则跳转到名为 repeat 的标签处(即 t repeat)
:repeat 标签

 

 

 

九·、综合案例(案例摘自 GNU sed 官网)

综合案例 1:重命名文件名为小写

[jacob@localhost ~] #cat   sed.sh
#! /bin/sh
# rename files to lower/upper case...
#
# usage:
#       move-to-lower *
#       move-to-upper *
# or
#       move-to-lower -R .
#       move-to-upper -R .
#
help()
{
cat << eof
Usage: $0 [-n] [-r] [-h] files...
-n           do nothing, only see what would be done
-R           recursive (use find)
-h           this message
files     files to remap to lower case
Examples:
$0 -n *               (see if everything is ok, then...)
$0 *
$0 -R .
eof
}
apply_cmd='sh'
finder='echo "$@" | tr " " "\n"'
files_only=
while :
do
case "$1" in
-n) apply_cmd='cat' ;;
-R) finder='find "$@" -type f';;
-h) help ; exit 1 ;;
*) break ;;
esac
shift
done
if [ -z "$1" ]; then
echo Usage: $0 [-h] [-n] [-r] files...
exit 1
fi
LOWER='abcdefghijklmnopqrstuvwxyz'
UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
case `basename $0` in
*upper*) TO=$UPPER; FROM=$LOWER ;;
*)             FROM=$UPPER; TO=$LOWER ;;
esac
eval $finder | sed -n '
# remove all trailing slashes
s/\/*$//
# add ./ if there is no path, only a filename
/\//! s/^/.\//
# save path+filename
h
# remove path
s/.*\///
# do conversion only on filename
y/'$FROM'/'$TO'/
# now line contains original path+file, while
# hold space contains the new filename
x
# add converted file name to line, which now contains
# path/file-name\nconverted-file-name
G
# check if converted file name is equal to original file name,
# if it is, do not print nothing
/^.*\/\(.*\)\n\1/b
# now, transform path/fromfile\n, into
# mv path/fromfile path/tofile and print it
s/^\(.*\/\)\(.*\)\n\(.*\)$/mv "\1\2" "\1\3"/p
' | $apply_cmd
综合案例 2:获取 bash 环境变量
#!/bin/sh
set | sed -n '
:x
# if no occurrence of ‘=()’ print and load next line
/=()/! { p; b; }
/ () $/! { p; b; }
# possible start of functions section
# save the line in case this is a var like FOO="() "
h
# if the next line has a brace, we quit because
# nothing comes after functions
n
/^{/ q
# print the old line
x; p
# work on the new line now
x; bx

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 sed文件块处理

 

文件块处理动做

 

操做符    用途            指令示例
i      行前插入文本    2iYY 在第2行以前添加文本行”YY”
                 4,7iYY 在第4-7行的每一行前添加文本行
a    行后插入文本       2aYY 在第2行以后添加文本
                 /^XX/aYY 在以XX开头的行以后添加文本
c    替换当前行      2cYY 将第2行的内容修改成”YY”

 

sed逐字符替换

操做y操做可实现逐个字符替换

 

 

修改后的文本有多行时

 

 

以换行符\n分隔
或者,以\强制换行

 

 

 

 

 sed行替换的应用

 

 

 

 sed行替换的应用:

找到主机名配置中的HOSTNAME行

整行替换为新的主机名配置

 

 

 

 

 

 

 

 

 

[root@localhost shell]# grep ^HOSTNAME /etc/sysconfig/network
HOSTNAME=localhost.localdomain              //修改前
[root@localhost shell]# sed -i '/HOSTNAME/cHOSTNAME=mysvr.tarena.com' /etc/sysconfig/network         //整行替换操做
[root@localhost shell]# grep ^HOSTNAME                         /etc/sysconfig/networkHOSTNAME=mysvr.tarena.com         //修改后

 

 

 

 

 

 

 

 

sed导入导出

 

 

导入导出动做

 

r动做应结合-i选项才会存入,不然只输出
w 动做以覆盖的方式另存为新文件

 

例子:

操做符      用途        指令示例
r      读入其余文件      3r b.txt 在第3行下方插入文件b.txt
                   4,7r b.txt 在第4-7每一行后插入文件b.txt
w      写入其余文件      3w c.txt 将第3行另存为文件c.txt
                   4,7w c.txt 将第4-7行另存为文件c.txt

 

sed复制剪切

 

模式空间

 

存放当前处理的行,将处理结果输出
若当前行不符合处理条件,则原样输出
处理完当前行再读入下一行来处理

 

 保持空间

 

 

做用相似于'剪切板'
默认存放一个空行(换行符\n)

 

主要处理动做

 

复制到剪贴板:

 


H    模式空间 ---[追加]--->保持空间
h    模式空间 ---[覆盖]--->保持空间

 

 

 

读取剪贴板内容并粘贴:

 

G    保持空间 ---[追加]--->模式空间
g    保持空间 ---[覆盖]--->模式空间

 

 

 

 

 

 

 

 sed 的工做过程:

 

把要处理的行先读入本身模式空间,而后用处理动做处理,处理完后输出处理后的结果,并把源数据输出,而后读入下一行到模式空间进行处理
* 要处理的数据必须在模式空间,且模式空间不存储数据
保持空间 保持空间里默认只保存一个换行符号\n
要想存数据放到保持空间里,要手动把数据存进来了才能够;
 保持空间只负责存储数据,不会输出数据
要想保持空间里的数据被sed处理,必须手动把保存空间里的数据调入模式空间
* 保持空间只负责存储数据 放在保持空间里的数据不会被输出也不会被处理。保持空间里默认只保存一个换行符号\n

 

 

sed流控制 

 

参数选项      注释
!取反操做    根据定址条件取反
n读下一行    读入下一行进行处理(产生隔行的效果)

 

 

 

 

 

 

 

 

 

sed练习:

1、删除/etc/grub.conf文件中行首的空白符;
sed -r 's@^[[:spapce:]]+@@g' /etc/grub.conf
2、替换/etc/inittab文件中"id:3:initdefault:"一行中的数字为5;
sed 's@\(id:\)[0-9]\(:initdefault:\)@\15\2@g' /etc/inittab
3、删除/etc/inittab文件中的空白行;
sed '/^$/d' /etc/inittab
4、删除/etc/inittab文件中开头的#号;
sed 's@^#@@g' /etc/inittab
5、删除某文件中开头的#号及后面的空白字符,但要求#号后面必须有空白字符;
sed -r 's@^#[[:space:]]+@@g' /etc/inittab
6、删除某文件中以空白字符后面跟#类的行中的开头的空白字符及#
sed -r 's@^[[:space:]]+#@@g' /etc/inittab
7、取出一个文件路径的目录名称;
echo "/etc/rc.d/" | sed -r 's@^(/.*/)[^/]+/?@\1@g'    
基名:
echo "/etc/rc.d/" | sed -r 's@^/.*/([^/]+)/?@\1@g'    
相关文章
相关标签/搜索