1 简介

sed 是一个流编辑器(Stream EDitor)。流编辑器对输入流(文件或者是来自于管道的输入)进行基本的文件转换。虽然在某些方面类似于脚本化编辑器(例如 ed ),但是 sed 通过对输入进行一次遍历来工作,因此更高效。比较大的区别是 sed 能在管道中筛选文本。

2 运行

2.1 概述

一般的 sed 用法:

sed SCRIPT INPUTFILE...

例,将 input.txt 中的‘hello’全部替换成‘world’:

sed 's/hello/world/' input.txt > output.txt

如果已没有指定INPUTFILE或者INPUTFILE是 - sed 将处理标准输入的内容,下述命令是等价的:

sed 's/hello/world/' input.txt > output.txt
sed 's/hello/world/' < input.txt > output.txt
cat input.txt | sed 's/hello/world/' - > output.txt

sed 默认是将处理结果写到标准输出。使用 -i 参数则是直接修改文件。另见 W s///w 命令将输出写入到其他文件。下述命令将会修改file.txt文件而不会产生任何输出:

sed -i 's/hello/world' file.txt

默认情况下, sed 打印所有的输入(修改和删除除外,比如命令 d )。用 -n 限制输出, p 打印特定行。以下命令只打印输入文件的第45行:

sed -n '45p' file.txt

sed 将多个输入文件作为一个长的输入流,在下述例子中,将打印第一个文件(one.txt)的第一行和第三个文件(three.txt)的最后一行。可用 -s 反转。

sed -n  '1p ; $p' one.txt two.txt three.txt

若没有指定 -e 或者 -f 选项, sed 使用第一个非选项参数作为执行脚本,接下来的非选项参数作为输入文件。如果使用 -e 或者 -f 指定一个执行脚本,其它所有的非参数选项将作为输入文件。 -e -f 可以结合使用,并且可以多次出现(最终的执行脚本是这些单独脚本连接后的结果)。 下述例子是等价的:

sed 's/hello/world/' input.txt > output.txt
sed -e 's/hello/world/' input.txt > output.txt
sed --expression='s/hello/world/' input.txt > output.txt
echo 's/hello/world/' > myscript.sed
sed -f myscript.sed input.txt > output.txt
sed --file=myscript.sed input.txt > output.txt

2.2 命令行选项

调用 sed 的完整格式是:

sed OPTIONS... [SCRIPT] [INPUTFILE...]

有如下的命令行选项:

--version 打印 sed 的版本和版权信息,然后退出

--help 打印一个使用信息,简要总结这些命令行选项和错误报告地址,然后退出。

-n --quiet --slient 默认情况下,sed在每个循环结束时通过脚本打印出模式空间(请参阅参考资料)。这些选项禁用了这种自动打印功能,sed只有在通过p命令明确告知时才会产生输出。

-e script --expression=script 将脚本中的命令添加到处理输入时要运行的一组命令中。

-f script-file --file=script-file 将包含在文件脚本文件中的命令添加到处理输入时要运行的一组命令中。

-i[SUFFIX] --in-place[=SUFFIX] 此选项指定编辑源文件。 sed 通过创建临时文件并将输出发送到此文件而不是标准输出来完成此操作。

这个选项意味着 -s

到达文件末尾时,临时文件将被重命名为输出文件的原始名称。扩展名(如果提供)用于在重命名临时文件之前修改旧文件的名称,从而创建备份副本2)。
该规则如下:如果扩展名不包含_,则将其作为后缀追加到当前文件名的末尾;如果扩展名包含一个或多个_字符,则每个星号都将替换为当前的文件名。这使您可以将前缀添加到备份文件中,而不是后缀,甚至将原始文件的备份副本放到另一个目录(假定该目录已经存在)。

如果没有提供扩展名,原始文件将被覆盖而不进行备份。

-l N --line-length=N 指定每行默认换行长度,长度为0表示用不换行,默认值为70。

--posix GNU sed包括POSIX sed的几个扩展。为了简化书写便携式脚本,这个选项禁用了本手册文档中的所有扩展,包括附加的命令。大多数扩展接受不在POSIX规定的语法之外的sed程序,但其中的一些(如Reporting Bug中描述的N命令的行为)实际上违反了标准。如果只想禁用后一种扩展,则可以将POSIXLY_CORRECT变量设置为非空值。

-b --binary 此选项在每个平台上都可用,但仅在操作系统区分文本文件和二进制文件的情况下才有效。当与MS-DOS,Windows的情况一样时,Cygwin文本文件由回车符和换行符分隔的行组成, sed 不会看到结尾的 CR 。当指定这个选项时, sed 将以二进制模式打开输入文件,因此不要求这个特殊处理,并且考虑以换行结束的行。

--follow-symlinks 此选项仅在支持符号链接的平台上可用,并且仅在指定选项 -i 时才有效。在这种情况下,如果命令行中指定的文件是符号链接, sed 将跟随链接并编辑链接的最终目标。默认行为是破坏符号链接,以便链接目标不会被修改。

-E -r --regexp-extended 使用扩展正则表达式而不是基本正则表达式。扩展的正则表达式是那些 egrep 接受的;通常有较少的反斜杠,表达更清楚。从历史上看,这是一个GNU扩展,但是 -E 扩展已经被添加到 POSIX标准 ,因此使用 -E 来实现可移植性。GNU的sed已经接受 -E 作为未记录选项, BSD 的sed也已经接受 -E 很长一段时间了,但使用 -E 的脚本可能无法移植到其他较老的系统。请参阅扩展正则表达式。

-s --separate 默认情况下,sed会将命令行中指定的文件视为一个连续的长流。这个GNU的sed的扩展允许用户将它们视为单独的文件:范围地址(如’/abc/,/def/’)不能跨越多个文件,行号是相对于每个文件的开始,$是指到每个文件的最后一行,并且从 R 命令调用的文件在每个文件的开始处倒回。

--sandbox 在沙箱模式下, e/w/r 命令被拒绝——包含它们的程序将被中止而不运行。沙箱模式可确保 sed 仅在命令行上指定的输入文件上运行,并且不能运行外部程序。

-u --unbuffered 缓冲输入和输出尽可能最小。 (如果输入来自 tail -f 之类的输入,那么这个特别有用,而且希望尽快看到转换后的输出。)

-z --null-data --zero-terminated 将输入视为一组行,每个行以零字节(ASCII’NUL’字符)而不是换行符结尾。这个选项可以用于像 sort -z find -print0 这样的命令来处理任意文件名。 如果命令行中没有给出 -e -f --expression --file 选项,则命令行上的第一个非选项参数将被视为要执行的脚本。 如果在处理完上面的命令行参数后仍然存在,则这些参数将被解释为要处理的输入文件的名称。文件名“-”是指标准输入流。如果没有指定文件名称,标准输入将被处理。

2.3 退出状态

零退出状态指示成功,非零值表示失败。 GNU sed退出状态,但返回以下错误值: 0 成功完成 1 命令无效,语法无效,正则表达式无效或与 --posix 一起使用的GNU sed的扩展命令。 2 命令行上指定的一个或多个输入文件无法打开(例如,如果找不到文件,或者读权限被拒绝)。处理继续与其他文件。 4 一个I/O错误或运行时严重的处理错误,GNU sed立即中止。 另外,命令q和Q可以用来以一个自定义的退出代码值来终止sed(这是一个GNU sed扩展):

$ echo | sed 'Q42' ; echo $?

3 sed 脚本

3.1 sed 脚本概述

一个 sed 程序由一个或多个 sed 命令组成,这些命令由一个或多个-e,-f,–expression和–file选项传入,或者如果没有这些选项,则由第一个非选项参数传入。本文档当中所有的 sed 脚本 均表示所有传入脚本连接后的结果。 sed 命令语法如下:

[addr]X[options]

X是单字符的 sed 命令。[addr]是一个可选的行地址。如果[addr]被指定,则X命令只在指定行执行。[addr]可以是一个行号,一个正则表达式也可是一个行范围(参见 sed 地址 )。其它选项被用于某些 sed 命令。 下述的例子将在输入文件中删除第30至35行。 30,35 是地址范围。 d 是删除命令:

sed '30,35d' input.txt > output.txt

以下示例将打印所有输入,直到找到以“foo”开头的行。如果找到这一行, sed 将以状态42退出。如果没有找到这样的行(并且没有发生其他错误),则sed将以状态0退出。 /^foo/ 是一个正则表达式地址, q 是退出命令, 42 是命令选项。

sed '/^foo/q42' input.txt > output.txt

脚本或脚本文件中的命令可以用分号(;)或换行符(ASCII 10)分隔。可以使用 -e -f 选项指定多个脚本。 以下例子都是等价的。它们执行两个 sed 操作:删除与正则表达式 /^foo/ 匹配的所有行,并用‘ world ’替换所有出现的字符串‘ hello ’:

sed '/^foo/d ; s/hello/world/' input.txt > output.txt
sed -e '/^foo/d' -e 's/hello/world/' input.txt > output.txt
echo '/^foo/d' > script.sed
echo 's/hello/world/' >> script.sed
sed -f script.sed input.txt > output.txt
echo 's/hello/world/' > script2.sed
sed -e '/^foo/d' -f script2.sed input.txt > output.txt