当前位置: 首页 > 工具软件 > GNU sed > 使用案例 >

GNU sed 4.5 版参考文档全文翻译 各命令和随带20个示例详细解析(三)

罗智刚
2023-12-01

GNU sed 一个流编辑器(三)

—— 版本 4.5,2018年3月30日

作者:Ken Pizzini, Paolo Bonzini
译者:浙江省杭州市 Samuel        

标签:Linux sed 4.5版本 帮助文档 参考文档 全文翻译 随带20个示例 详细解析

版权声明:本文为博主原创译文,未经博主允许不得转载。https://blog.csdn.net/qq_39785418/article/details/89787640

3、sed脚本

3.8 多命令语法

  在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

3.8.1需要换行的命令

  下面的命令不能使用分号分隔,需要换行:

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
				

4、 地址:选择行

4.1 地址摘要

  地址条件决定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

4.2 通过数字选择行

  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
				

4.3 通过文本匹配选择行

  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

4.4 范围地址

  可以通过由逗号(,)分隔的两个地址来指定地址范围。地址范围匹配从第一个地址匹配的位置开始的行,并继续直到第二个地址匹配为止(含前后两个地址):

$ 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可以是一个行号或者一个正则表达式。

GNU sed 4.5 版参考文档全文翻译 各命令和随带20个示例详细解析(四)

 类似资料: