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 $?
42
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
命令a
,c
,i
由于它们的语法,所以不能使用作为命令分隔符的分号,因此应该用换行符结尾或是放在脚本或脚本文件的末尾。命令也能以可选的不重要的空白字符开头。请参阅多命令语法。
3.2 sed
命令汇总
GNU sed支持以下命令。有些是标准的POSIX命令,而另一些是GNU扩展。每个命令的详细信息和示例在以下部分。(助记符)显示在括号内。
a\
text
在一行之后追加文本。
a text
在一行之后追加文本(替代语法)。
b label
无条件地分支标签。标签可以省略,在这种情况下下一个周期开始。
c\
text
用文本替换(更改)行。
c text
用文本替换(更改)行(替代语法)。
d
删除模式空间;立即开始下一个周期。
D
如果模式空间包含换行符,则删除模式空间中的文本直到第一个换行符,然后用结果模式空间重新启动循环,而不读取新的输入行。如果模式空间不包含换行符,就像d
命令一样,开始一个正常的新周期。
e
执行在模式空间中找到的命令,并用输出替换模式空间;尾随的换行符被压制。
F
(filename)打印当前输入文件的文件名(带有换行符)。
g
用占位空间的内容替换模式空间的内容。
G
向模式空间的内容追加换行符,然后将保留空间的内容追加到模式空间的内容。
h
(hold)将保持空间的内容替换为模式空间的内容。
H
追加一个换行符到保存空间的内容,然后将模式空间的内容追加到保存空间的内容。
i\
text
在一行之前插入文本。
i text
在一行之前插入文本(替代语法)。
l
以确定的形式打印模式空间
n
(next)如果未禁用自动打印,请打印模式空间,然后用下一行输入替换模式空间。如果没有更多的输入,那么sed
退出而不处理任何更多的命令。
N
向模式空间添加换行符,然后将下一行输入添加到模式空间。如果没有更多的输入,那么sed
退出而不处理任何更多的命令。
p
打印模式空间
P
打印模式空间,直到第一个<换行符>。
q[exit-code]
(quit)退出sed而不处理任何更多的命令或输入。
Q[exit-code]
(quit)该命令与q相同,但不会打印模式空间的内容。像q一样,它提供了将退出代码返回给调用者的功能。
r filename
读文本文件的一个文件。
R filename
在当前周期结束时排队读取一行文件名并将其插入到输出流中,或读取下一个输入行时。
s/regexp/replacement/[flags]
(substitue)匹配正则表达式和模式空间的内容。如果找到,用替换替换匹配的字符串。
t label
(test)分支只有在自上次输入行被读取或进行了条件分支以来已成功进行替换时才进行标记。标签可以省略,在这种情况下下一个周期开始。
T label
(test)分支只有在自上一条输入行被读取或条件分支被采用以来没有成功的替换时才被标记。标签可以省略,在这种情况下下一个周期开始。
v [version]
(version)这个命令什么都不做,但是如果不支持GNU sed扩展,或者如果请求的版本不可用,sed会失败
w filename
将模式空间写入文件名。
W filename
写入给定的文件名模式空间的部分直到第一个换行符
x
交换持有和模式空间的内容。
y/src/dst/
将模式空间中所有与任何源字符匹配的字符与目标字符中的对应字符进行替换。
z
(zap)该命令清空模式空间的内容。
#
注释,直到下一个换行。
{ cmd ; cmd ... }
将几个命令一起分组。
=
打印当前输入行号(带有换行符)。
: label
指定分支命令(b,t,T)的标签位置。
3.3 s
命令
s
命令(替换)在sed中可能是最重要的,并且有很多不同的选项。
s
命令的语法是s/regexp/replacement/flags
。
它的基本概念很简单:s
命令尝试将模式空间与提供的正则表达式regexp
进行匹配;如果匹配成功,则匹配的那部分模式空间被replacement
替换。
有关regexp
语法的详细信息,请参阅正则表达式地址。
replacement
可以包含\n(n是从1到9的数字,包括1和9),它是指包含在第n个(及其匹配的\)之间的匹配部分。此外,replacement
可以包含引用模式空间的整个匹配部分的非转义&
字符。 在任何给定的s
命令中,/
字符可以被任何其他单个字符统一替换。 /
字符(或任何其他替代字符)可以出现在regexp
或replacement
中,只要前面有一个\
字符。 最后,作为一个GNU sed
扩展,你可以包含一个由反斜杠和字母L
,l
,U
,u
或E
中的一个组成的特殊序列。含义如下: \L
替换为小写,直到找到\U
或\E
, \l
将下一个字母转为小写, \U
替换为大写,直到找到\L
或\E
, \u
将下一个字符转为大写, \E
停止由\L
或者\U
开始的大小写转换 使用g
标志时,大小写转换不会从正则表达式的一次出现传播到另一次。例如,当在模式空间中使用a-b-
执行以下命令时:
s/\(b\?\)-/x\u\1/g
输出是axxB
。当替换第一个’-
时,\u
序列只影响\1
的空白替换。它不会影响用xB
替换b-
时添加到模式空间的x
字符。 另一方面,如果\l
和\u
之后是空替换,确实会影响替换文本的其余部分。在模式空间中使用a-b-
,使用以下命令:
s/\(b\?\)-/\u\1x/g
将用X
(大写)替换-
和用Bx
代替b-
。如果这种行为是不可取的,那么在这种情况下,可以通过在\1
之后添加一个\E
序列来阻止它。 要在最终替换中包含文字\
,&
或换行符,请务必在所需的\
,&
或换行符之前加上转义符\
。 s
命令后面可以跟零个或多个以下flags
:
g
将所有匹配的替换应用于正则表达式,而不仅仅是第一个。
number
只替换正则表达式的第number
个匹配。 s
命令中的交互注意:POSIX标准没有规定当你将g
和number
修饰符混合时会发生什么,而且目前在sed
实现中没有统一的意见。
对于GNU sed
,交互被定义为:在第number
之前忽略匹配,然后匹配并替换第number
后的所有匹配。 p
如果进行替换,则打印新的模式空间。 注意:当指定p
和e
选项时,两者的相对顺序会产生非常不同的结果。一般来说,ep
(评估然后打印)是你想要的,但是反过来操作对于调试是有用的。由于这个原因,当前版本的GNU sed
特别解释了在e
之前和之后的p
选项的存在,在评估之前和之后打印模式空间,而s
命令的一般标志只显示其效果一次。这种行为虽然有文件记录,但在将来的版本中可能会发生变化。
w filename
如果进行了替换,则将结果写入指定的文件。作为一个GNU sed扩展,支持两个特殊的filename值:/dev/stderr
,将结果写入标准错误,/dev/stdout
写入标准输出。 e
这个命令允许将命令从shell命令输入到模式空间。如果已经进行替换,则会执行在模式空间中找到的命令,并将模式空间替换为其输出。尾随的换行符被压制;如果要执行的命令包含NUL字符,则结果是不确定的。这是一个GNU sed
扩展。
I
i
用于正则表达式匹配的I
修饰符是一个GNU扩展,它以不区分大小写的方式使sed
匹配正则表达式。
M
m
正则表达式匹配的M
修饰符是一个GNU sed
扩展,它指示GNU sed
在多行模式下匹配正则表达式。该修饰符使^
和$
分别匹配(除了正常行为)换行之后的空字符串和换行之前的空字符串。有特殊的字符序列(\`
和\'
)总是匹配缓冲区的开始或结束。此外,句点字符在多行模式下与换行字符不匹配。