Sed 使用参考

简介

Sed 是一款流编辑工具,用来对文本进行过滤与替换操作。Sed 一次仅读取一行内容来对某些指令进行处理后输出,所以 Sed 处理大数据文件是很方便快捷的。

Sed 的工作流程是:首先,通过文件或者管道读取文件内容。Sed 默认并不直接修改源文件,而是将读入的内容赋值到缓冲区中,这被称之为模式空间,所有指令操作都是在模式空间中进行。然后, Sed 根据相应的指令对模式空间中的内容进行处理并输出,默认输出至标准输出(即屏幕)。

基本语法格式

Sed 从文件中读取数据,如果没有输入文件,则默认对标准输入进程数据进行处理,基本指令是第一个非 “-” 开头的参数,具体语法格式如下。

用法: sed [选项]... {脚本指令} [输入文件]...
选项: --version 显示 sed 版本
             --help 显示帮组文档
             --n, --quit, --silent 静默输出
             -e script 允许多个脚本指令被执行
             -f script-file 从文件中读取脚本指令,对编写自动脚本程序很实用
             -i, --in-place 直接修改源文件,慎用
             -1N 指定1指令可以输出的行长度,1指令为输出非打印字符
             --posix 禁用 GNU sed 扩展功能
             -r 在脚本指令中使用扩展正则表达式
             -s, --separate 默认情况下,sed 将把输入的多个文件名作为一个长的连续的输入流。而 GNU sed 则允许将其当作单独的文件
             -u, --unbuffered 最低限度的缓存输入与输出

指令与脚本

常用指令汇总

Sed 常用的脚本指令如下所示:

a\ 在当前行下面插入文本。
i\ 在当前行上面插入文本。
c\ 把选定的行改为新的文本。
d 删除,删除选择的行。
D 删除模板块的第一行。
s 替换指定字符
h 拷贝模板块的内容到内存中的缓冲区。
H 追加模板块的内容到内存中的缓冲区。
g 获得内存缓冲区的内容,并替代当前模板块中的文本。
G 获得内存缓冲区的内容,并追加到当前模板块文本的后面。
l 列表不能打印字符的清单。
L 同l,不显示非打印字符
n 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。
N 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
p 打印模板块的行。
P(大写) 打印模板块的第一行。
q 退出Sed。
b lable 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。
r file 从file中读行。
t label if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
T label 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
w file 写并追加模板块到file末尾。
W file 写并追加模板块的第一行到file末尾。
! 表示后面的命令对所有没有被选定的行发生作用。
= 打印当前行号码。
# 把注释扩展到下一个换行符以前。

部分指令详解

(1)替换指令(s, substitution)

指令格式:

[address]s/pattern/replacement/flags

address 为操作地址,s为替换指令,/pattern/匹配需要替换的内容,/replacement/为替换的内容,flags标记可以是如下内容:

1 - 512 之间的数字,表示对模式空间中指定模式的第 n 次出现进行替换。如一行中有3个A,而只想替换第二个A。

对模式空间的所有匹配进行全局更改。没有 g 则只有第一次匹配被替换。如一行中有3个A,则仅替换第一个A。

打印模式空间的内容,即表示打印行。与-n选项一起使用可以只打印匹配的行。

将模式空间的内容写到文件 file 中。 即表示把行写入一个文件。

replacement 为字符串,用来替换这则表达式匹配的内容。在 replacement 部分,下列字符有特殊含义:

&   用正则表达式匹配的内容进行替换
\n  匹配第 n 个子串,该子串之前在 pattern 中用 `\(\)`指定,即正则表达式分组。
\    转义(转义替换部分包含:&、\等)

示例:

1、替换未闭合的 html body 标签:

sed 's/body/\/body/2' file

2、将文件中所有 yse 替换为 yes:

sed 's/yse/yes/g' file

3、输出文件中含 huoty 的行:

sed -n '/huoty/p' file

4、将所有的单词用中括号 [] 包裹起来:

echo this is a test line | sed 's/\w+/[&]/g'

(2)删除命令(d, delete)

删除命令用于删除匹配的行,而且删除命令还会改变 sed 脚本中命令的执行操作顺序,因为匹配的行一旦被删除,模式空间将变为“空”,自然不会再执行哪个 sed 脚本后续的命令。删除命令会导致读取新的输入行(下一行),而 sed 脚本中的命令则从头开始执行。需要注意的是删除时是删除整行,而不是删除匹配的内容(如要删除匹配的内容,可以使用替换)。

示例,删掉空白行:

sed '/^$/d' file

(3)转换指令(y)

按字符转换(Transform)的语法格式为:[address]y/yousource-chars/dest-chars/,其中,address 用于定位需要修改的行,source-chars 为需要修改的字符,dest-chars 为准备替换的字符。

示例,就文件中的 china 转换为大写:

sed '/china/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKNOPQRSTUVWXYZ/' file

定界符

在以上列举的一些简单示例中,定界符都是 /。实际上,在 sed 中可以用任意字符作为定界符。sed 会自动将第一个出现的非命令字符作为定界符。示例如下:

sed 's:test:TEXT:g' file

sed 's|test|TEXT|g' file

定界符出现在样式内部时,需要进行转义:

sed 's/\/bin/\/usr\/local\/bin/g' file

命令组合

用时候我们可能会对一个文件或者输入做连续的 sed 处理,例如:

sed '表达式' | sed '表达式' | sed '表达式'

其实,我们可以将所有的表达式用分号(;)组合起来,即:

sed '表达式; 表达式;表达式'

分号的含义就是将前面的处理完结果传给后边的表达式继续处理。

我们也可以对多个命令用大括号 {} 进行组合,然后作用于同一匹配地址,即:

address{commad1; command2; command3}

也可以放在多行:

address{
    commad1
    command2
    command3
}

示例,如果文件中含 test 的行,则将其下一行的 aa 替换为 bb:

sed '/test/{ n; s/aa/bb/; }' file

选定行的范围

选择要处理行的范围,可以用逗号(,)来分割。例如选定所有在模板test和check所确定的范围内的行:

sed -n '/test/,/check/p' file

打印从第5行开始到第一个包含以test开始的行之间的所有行:

sed -n '5,/^test/p' file

对于模板test和west之间的行,每行的末尾用字符串aaa bbb替换:

sed '/test/,/west/s/$/aaa bbb/' file

Sed 常用格式

sed命令行常用的基本格式大致有一下三种形式:

(1)Sed [options] 'script' file1 file2 ...

script结构为 /PATTERN/action,PATTERN 为正则表达式,action为要执行的动作。例如:sed ‘/[[:upper:]]/d’ binary.sh 表示删除所有的大写字母的行。script的结构还可以是 /PATTERN1/,/PATTERN2/action,这表示从第一次被PATTERN1匹配到的行到第一次被PATTERN2匹配到的中间的所有行执行action动作。

这里需要注意的是,当进行字符串替换时,需要在PATTERN前加上s动作,并在末尾加上替换的范围,例如: sed 's/abc/123/g' 表示将匹配到的字符串 abc 替换成 123,g 表示替换所有的行。

(2)Sed [options] –f scriptfile file1 file2 ...

scriptfile表示脚本文件,即 sed 支持将要执行的操作写在文件里边,然后通过使用 -f 参数来加载文件。

(3)Sed [options] 'ADDR1,ADDR2command' file1 file2 ...

该格式应用于以行为单位的操作,例如:

sed ’1,2d’ file

就可以将file的前两行删除并显示出来,但是它不会改变源文件。

Sed ‘1,2!d’ file

表示删除除第一行和第二行之外的所有行。

注: 在这种格式中的 & 表示引用前面匹配到的所有字符。并且在该种格式中可以引入分组。

示例:

$ sed 's/bc/-&-/' testfile

这里表示在匹配到bc字符两端加上字符'-'

$ sed 's/ /-\1-~\2~/' testfile

这里的 \1 和 \2 表示正则表达式的分组1和分组二所匹配的内容。

Sed 高级应用

正常的 Sed 数据处理流程是读取文档的一行至模式空间,然后对该行应用相应的 Sed 指令。当指令完成后输出该行并清空模式空间,一次循环读入文档的下一行数据,直至文档数据结尾。然而在真实环境中的数据可能并不会那么有规律,有时我们会把数据分多行写入文档,如:

姓名:张三
邮箱:zhangsan@gmail.com
姓名:李四
邮箱:lisi@gmail.com

从上面的模板文件中可以看出,实际每两行位一条完整的记录,而此时如果需要用 Sed 对文档进行处理,就需要对 Sed 工作流程进行人工干预。

多行操作 Next

Next(N)指令通过读取新的输入行,并将它追加至模式空间的现有内容之后,来创建多行模式空间。模式空间的最初内容与新的输入含之间用换行符分隔。在模式空间中插入的换行符可以用 \n 匹配。

列举一个范例,范例所用样本文件如下(test.txt):

Name:Huoty
Mail:huoty@gmail.com
Name:Konghy
Mail:konghy@163.com

我们要做的处理是,当读入的内容与 Name 匹配时,立刻读取下一行,再输入模式空间中的内容。处理脚本如下所示(sed.sh):

#n
/Name/{
N
L
}

其中,#n 放在脚本文件中表示屏蔽自动输出,L表示不打印非打印字符(小写 l 标识打印非打印字符),即行尾的 \n。用 sed 执行操作如下:

sed -f sed.sh test.txt

多行操作 Print

Print(p)表示仅输出多行模式空间中的第一部分直到第一个插入的 \n 换行符为止。如模式空间中的内容为 “aaa\nbbb”,则 P 只输出 aaa。

多行删除 Delete(D)

Delete 删除模式空间中直到第一个插入的换行符(\n)前的内容。由于 d 命令的作用是删除模式空间中的内容并读取新的输入行,而如果 sed 在 d 指令后还有多条命令,则余下的指令将不再执行。而返回第一条指令对新度入行进行处理。多行指令 D 则不会读入因的行,而是放回 sed 脚本的顶端,使得剩余指令继续应用于模式空间中的剩余部分内容。

Hold(h,H), Get(g,G)

Sed 还有一个称为保持空间(hold space)的缓冲区。模式空间的内容可以复制到保持空间,保持空间同样可以复制到模式空间。由一组 Sed 命令用于两者之间移动数据:

Hold(h|H)  将模式空间的内容复制或者追加到保持空间
Get(g|G)  将保持空间的内容复制或者追加到模式空间
Exchange(x)  交换保持空间与模式空间中的内容

举一个使用范例,样本文件如下(test.txt):

aaa
bbb
ccc
ddd

Sed 教程文件如下(sed.sh):

/aaa/{
h
d
}
/ccc/{
G
}

执行处理命令:

sed -f sed.sh test.txt

结果如下所示:

bbb
ccc
aaa
ddd