版权声明:本文为博主原创译文,未经博主允许不得转载。https://blog.csdn.net/qq_39785418/article/details/89787640
在sed程序中有几种方法可以指定多个命令。
当使用“-f”选项从指定脚本文件中读取并运行sed脚本时,使用换行来分隔命令是最自然的。
在命令行上,所有sed命令可以用换行分隔。或者,您也可以把每个命令作为“-e”选项的参数:
$ seq 6 | sed ‘1d
3d
5d’
2
4
6
$ seq 6 | sed -e 1d -e 3d -e 5d
2
4
6
分号“;”可以用于分隔简单的命令:
$ seq 6 | sed ‘1d; 3d; 5d’
2
4
6
这些命令例如:“{”、“}”、“b”、“t”、“T”可以用分号‘;’分隔,但是这是GNU sed的扩展,不具有移植性。
$ seq 4 | sed '{1d;3d}'
2
4
$ seq 6 | sed '{1d;3d};5d'
2
4
6
在b、t、T命令中使用的标签:读取分号之前的命令。标签前面和后面的空白都会忽略。在下面的例子中,标签是“x”。第一个例子在GNU sed中可以正常执行。第二个是可移植的等价物。有关分支和标签的更多信息,请参见第6.4节[分支和流控制]。
$ seq 3 | sed '/1/b x ; s/^/=/ ; :x ; 3d'
1
=2
$ seq 3 | sed -e '/1/bx' -e 's/^/=/' -e ':x' -e '3d'
1
=2
下面的命令不能使用分号分隔,需要换行:
a,c,i (append/change/insert)(追加、替换、插入)
a、c、i命令后面的所有字符都当作追加、更改、插入的文本。使用分号会导致您不希望出现
的结果:
$ seq 2 | sed ‘1aHello ; 2d’
1
Hello ; 2d
2
使用换行或者-e分隔命令:
$ seq 2 | sed -e 1aHello -e 2d
1
Hello
$ seq 2 | sed '1aHello
2d'
1
Hello
注意,在a、c之后立即指定要添加的文本“Hello”本身就是GNU sed扩展。一个可移植的、
兼容posix的替代方案是:
$ seq 2 | sed '1a\
Hello
2d'
1
Hello
# (comment)
#行开头到换行为止中的所有字符都忽略。
$ seq 3 | sed '# this is a comment ; 2d'
1
2
3
$ seq 3 | sed '# this is a comment
2d'
1
3
r, R, w, W (reading and writing files)(读取和写文件)
r、R、w、W命令把命令之后、行尾之前的所有字符都解析成文件名。如果发现空格、注释或分号,
它们会包含在文件名中,导致意外结果:
$ seq 2 | sed '1w hello.txt ; 2d'
1
2
$ ls -log
total 4
-rw-rw-r-- 1 2 Jan 23 23:03 hello.txt ; 2d
$ cat 'hello.txt ; 2d'
1
请注意,sed会默默地忽略r、R、w、W命令中的读/写错误,例如找不到文件。在下面的示例中,
sed尝试读取名为' hello.txt;N’ 的文件。文件找不到,错误被默默忽略:
$ echo x | sed '1rhello.txt ; N'
x
e (command execution)(命令执行)
e命令会把命令之后、行尾之前的所有字符都会送到shell当成命令执行。如果发现空格、注释或分号,
它们将包含在文件名中,导致意外结果:
$ echo a | sed '1e touch foo#bar'
a
$ ls -1
foo#bar
$ echo a | sed '1e touch foo ; s/a/b/'
sh: 1: s/a/b/: not found #(译者:命令没有找到)
a
s///[we] (substitute with e or w flags)(替换 使用e或w标志)
在替换成功后,w命令会把替换结果写到一个文件中,或者e命令把替换结果当成shell命令执行。
与r、R、w、W命令类似,如果发现空格、注释或分号,它们将包含在文件名中,导致意外结果:
$ echo a | sed 's/a/b/w1.txt#foo'
b
$ ls -1
1.txt#foo
地址条件决定sed命令在哪些行上执行。下面的命令只有在第144行中出现的第一个“hello”替换成“world”:
sed ‘144s/hello/world/’ input.txt > output.txt
如果没有指定地址,s命令在所有行上执行。下面的命令对输入文件的所有行中出现的第一个“hello”替换成“world”:
sed 's/hello/world/' input.txt > output.txt
地址可以包含正则表达式,以便根据行内容而不是行号来匹配行。下面的命令只有在包含“apple”的行上出现的第一个“hello”替换成“world”:
sed '/apple/s/hello/world/' input.txt > output.txt
地址范围由逗号(,)分隔的两个地址指定。地址可以是数字(行号)、正则表达式或两者的组合。下面的命令只在第4行到第17行(含4和17)之间的行上出现的第一个“hello”替换成“world”:
sed '4,17s/hello/world/' input.txt > output.txt
在命令字母之前,地址声明之后添加一个“!”字符对匹配状态取反。那就是说,如果“!”字符跟在一个地址或者一个地址范围之后,那么,只有没有匹配地址行会被选择。下面的命令只在不包含“apple”的行上出现的第一个“hello”替换成“world”:
sed ’/apple/!s/hello/world/’ input.txt > output.txt
下面的命令只在第1行到第3行(含1、3)以及第18(含)行开始到输入文件的最后一行为止的行上出现的第一个“hello”替换成“world”(例如:排除4到17行):
sed ’4,17! s/hello/world/’ input.txt > output.txt
sed脚本中的地址可以采用以下任何形式:
number 指定行号,只会匹配输入的那一行。(注意,除非指定了“-i”或“-s” 选项,否则sed会对所有输入文件中的行进行
连续计数。)
$ 此地址与输入的最后一个文件的最后一行匹配,或者在指定“-i”或“-s”选项时与每个文件的最后一行匹配。
first~step (开始~步长)这是GNU扩展。匹配从第first行开始的每个步长行。尤其是,当存在非负n、且当前行的行号等于
“first +(n * step)”时将被选择。因此,用1~2来选择奇数行,用0~2来选择偶数行;用2~3来选择从第二行开始的
每三行;用10~5来选择从第十行开始的每5行;用50~0来表示50只是一种模糊的方式。以下命令演示步长地址的用法:
$ seq 10 | sed -n ’0~4p’
4
8
$ seq 10 | sed -n ’1~3p’
1
4
7
10
GNU sed支持下列正则表达式地址。默认的正则表达式语法讲解在第5.3节[基础正则表达式(BRE)]。如果使用了“–E” 或“–r”选项,正则表达式语法讲解在第5.4节[扩展正则表达式(ERE)]。参见第5.2节[BRE比较ERE]。
/regexp/ 这将选择所有匹配正则表达式regexp的行。如果regexp本身包含任何“/”字符,其中的每一个“/”必须使用
反斜杠(\)转义。
下面的命令打印在“/etc/passwd”文件中以“bash”结尾的所有行:
(当然,还有很多其他方法可以做到这一点,例如:
grep ’bash$’ /etc/passwd
awk -F: ’$7 == "/bin/bash"’ /etc/passwd
)
sed -n ‘/bash$/p’ /etc/passwd
空正则表达式“//”重复上一个匹配成功的正则表达式(如果将空正则表达式传递给s命令,也具有相同的功能)。
请注意,正则表达式的修饰符(译者:标志)是在编译正则表达式时评估的,因此与空正则表达式一起指定
修饰符是无效的。
\%regexp%
(这个“%”符号可以由其他任意单个字符替代。)
这也会匹配正则表达式regexp,但允许使用与“/”不同的分隔符。如果regexp本身包含大量斜杠(/)时,
这尤其有用,因为它避免了对每个“/”进行冗长转义。如果regexp本身包含任何分隔符字符,则每个字符
都必须用反斜杠(\)转义。
下面的三个命令都是等价的,其中两个更换了分隔符。它们打印以 /home/alice/documents/ 开头的所有行:
sed -n ’/^\/home\/alice\/documents\//p’
sed -n ’\%^/home/alice/documents/%p’
sed -n ’\;^/home/alice/documents/;p’
/regexp/I
\%regexp%I
正则表达式的I修饰符是GNU扩展,它使regexp以不区分大小写的方式进行匹配。
在许多其他编程语言中,小写字符i用于不区分大小写的正则表达式匹配。但是,在sed中,i用于insert命令
(参见[insert command插入命令])。
观察下面例子的不同之处。
在这个示例中,“/b/I”是一个地址,使用I修饰符的正则表达式,d是删除命令:
$ printf "%s\n" a B c | sed ’/b/Id’
a
c
这里,“/b/”是一个正则表达式地址,i是插入命令。d是要插入的值。一行内容为d被插入到匹配行的上面:
$ printf "%s\n" a b c | sed ’/b/id’
a
d
b
c
/regexp/M
\%regexp/M
正则表达式的修饰符M是GNU sed的扩展。它指示GNU sed除了正常的行为之外,在多行模式下去匹配正则表达式。
该修饰符引起“^”和“$”分别匹配换行之后的空字符和换行之前的空字符。有一些特殊的字符序列(\‘和 \’)总是与
缓冲区的开始或结束相匹配。此外,“.”句点字符在多行模式下不匹配换行符。
正则表达式地址决定是否处理当前模式空间的内容。如果模式空间内容发生了改变(例如使用“s///”替换命令),
正则表达式会匹配变化后的文本。
下面的例子中,自动打印被“-n”禁止。“s/2/X/”替换命令改变了行包含的 2 到 X。/[0-9]/p命令匹配包含数字的行,
然后打印它们。由于第二行在“/[0-9]/”正则表达式起作用之前已经改变,所以,就不能匹配到,也不会打印出来:
$ seq 3 | sed -n 's/2/X/ ; /[0-9]/p'
1
3
可以通过由逗号(,)分隔的两个地址来指定地址范围。地址范围匹配从第一个地址匹配的位置开始的行,并继续直到第二个地址匹配为止(含前后两个地址):
$ seq 10 | sed -n ’4,6p’
4
5
6
如果第二个地址是regexp,那么检查结束匹配将从与第一个地址匹配行后面的行开始搜索:范围将始终跨越至少两行(当然输入流结束的情况除外)。
$ seq 10 | sed -n ’4,/[0-9]/p’
4
5
如果第二个地址是小于或等于第一个地址匹配行的数字,则只匹配的那一行:
$ seq 10 | sed -n ’4,1p’
4
GNU sed也支持一些特殊的两地址形式;所有这些都是GNU扩展:
0,/regexp/ 在地址规范中行号为0可以使用,如“0,/regexp/”,sed会试图在第一输入行上进行匹配。话句话说,
“0,/regexp/”与“1,/regexp/”功能类似,区别是地址“/regexp/”是否与第一输入行匹配。因为“0,/regexp/”
格式的“/regexp/”会从第一输入行开始搜索,如果匹配成功,则会结束地址范围。而“1,/regexp/”格式
的“/regexp/”会试图从第二输入行开始搜索,从而使地址范围扩展到该正则表达式匹配到的行为止。
请注意,这是0地址唯一有意义的地方;输入流中没有第0输入行,以任何其他方式给0地址的命令都会出错。
以下示例演示了从地址1开始与从地址0开始的区别:
$ seq 10 | sed -n ’1,/[0-9]/p’
1
2
$ seq 10 | sed -n ’0,/[0-9]/p’
1
addr1,+N 匹配第addr1行和addr1后面的N行。
$ seq 10 | sed -n ’6,+2p’
6
7
8
addr1,~N 匹配第addr1行及其后面的行,直到输入行号是n的倍数。以下
命令从第6行开始打印,直到下一行是4的倍数(即第8行):
$ seq 10 | sed -n ’6,~4p’
6
7
8
$ seq 100 | sed -n '8,~13p'
8
9
10
11
12
13
addr1可以是一个行号或者一个正则表达式。