本文共 10186 字,大约阅读时间需要 33 分钟。
[address]command # 第一种[line-address]command # 第二种第一种[address]是指可以为任意地址包括地址对,第二种[line-address]是指只能为单个地址。文中指出,例如i(后方插入命令)、a(前方追加命令)以及=(打印行号命令)等都属于第二种命令,但是实际上我在ArchLinux上测试,指定地址对也没有报错。不过为了兼容性,建议按作者所说的做。 以下是要介绍的全部基础命令:
名称 | 命令 | 语法 | 说明 |
---|---|---|---|
替换 | s | [address]s/pattern/replacement/flags | 替换匹配的内容 |
删除 | d | [address]d | 删除匹配的行 |
插入 | i | [line-address]i\ text | 在匹配行的前方插入文本 |
追加 | a | [line-address]a\ text | 在匹配行的后方插入文本 |
行替换 | c | [address]c\ text | 将匹配的行替换成文本text |
打印行 | p | [address]p | 打印在模式空间中的行 |
打印行号 | = | [address]= | 打印当前行行号 |
打印行 | l | [address]l | 打印在模式空间中的行,同时显示控制字符 |
转换字符 | y | [address]y/SET1/SET2/ | 将SET1中出现的字符替换成SET2中对应位置的字符 |
读取下一行 | n | [address]n | 将下一行的内容读取到模式空间 |
读文件 | r | [line-address]r file | 将指定的文件读取到匹配行之后 |
写文件 | w | [address]w file | 将匹配地址的所有行输出到指定的文件中 |
退出 | q | [line-address]q | 读取到匹配的行之后即退出 |
[address]s/pattern/replacement/flags其中[address]是指地址,pattern是替换命令的匹配表达式,replacement则是对应的替换内容,flags是指替换的标志位,它可以包含以下一个或者多个值:
$ echo "column1 column2 column3 column4" | sed 's/ /;/'column1;column2 column3 column4如果flags中包含g,则表示全局匹配:
$ echo "column1 column2 column3 column4" | sed 's/ /;/g'column1;column2;column3;column4如果flags中明确指定替换第n次的匹配,例如n=2:
$ echo "column1 column2 column3 column4" | sed 's/ /;/2'column1 column2;column3 column4当替换命令的pattern与地址部分是一样的时候,比如
/regexp/s/regexp/replacement/
可以省略替换命令中的pattern部分,这在单个编辑命令的情况下没多大用处,但是在组合命令的场景下还是能省不少功夫的,例如下面是摘自文中的一个段落: The substitute command is applied to the lines matching the address. If noaddress is specified, it is applied to all lines that match the pattern, aregular expression. If a regular expression is supplied as an address, and nopattern is specified, the substitute command matches what is matched by theaddress. This can be useful when the substitute command is one of multiplecommands applied at the same address. For an example, see the section "CheckingOut Reference Pages" later in this chapter.现在要在substitute command后面增加("s"),同时在被修改的行前面增加+号,以下是使用的sed命令:
$ sed '/substitute command/{s//&("s")/;s/^/+ /}' paragraph.txt这里我们用到了组合命令,并且地址匹配的部分和第一个替换命令的匹配部分是一样的,所以后者我们省略了,在replacement部分用到了&这个元字符,它代表之前匹配的内容,这点我们在后面介绍。执行后的结果为:
+ The substitute command("s") is applied to the lines matching the address. If noaddress is specified, it is applied to all lines that match the pattern, aregular expression. If a regular expression is supplied as an address, and no+ pattern is specified, the substitute command("s") matches what is matched by the+ address. This can be useful when the substitute command("s") is one of multiplecommands applied at the same address. For an example, see the section "CheckingOut Reference Pages" later in this chapter.替换命令的一个技巧是中间的分隔符是可以更改的(这个技巧我们在 中也曾经介绍过),这个技巧在有些地方非常有用,比如路径替换,下面是采用默认的分隔符和使用感叹号作为分隔符的对比:
$ find /usr/local -maxdepth 2 -type d | sed 's/\/usr\/local\/man/\/usr\/share\/man/'$ find /usr/local -maxdepth 2 -type d | sed 's!/usr/local/man!/usr/share/man!'替换命令中还有一个很重要的部分——replacement(替换内容),即将匹配的部分替换成replacement。在replacemnt部分中也有几个特殊的元字符,它们分别是:
[root@localhost ~]# grep -B 1 ACTIVE_CONSOLES /etc/sysconfig/init # What ttys should gettys be started on?ACTIVE_CONSOLES=/dev/tty[1-6]可以通过修改上面的配置来减少开机启动的时候创建的tty个数,比如我们只想要2个:
[root@localhost ~]# sed -r -i 's!(ACTIVE_CONSOLES=/dev/tty\[1-)6]!\12]!' /etc/sysconfig/init ACTIVE_CONSOLES=/dev/tty[1-2]其中-i参数表示直接修改原文件,-r参数是指使用扩展的正则表达式(ERE),扩展的正则表达式中分组的括号不需要用反斜杠转义"(ACTIVE_CONSOLES=/dev/tty\[1-)",这里[是有特殊含义的(表示字符组),所以需要转义。在替换的内容中使用\1来引用这个匹配的分组内容,1代表分组的编号,表示第一个分组。
[address]d删除命令可以用于删除多行内容,例如
1,3d
会删除1到3行。删除命令会将模式空间中的内容全部删除,并且导致后续命令不会执行并且读入新行,因为当前模式空间的内容已经为空。我们可以试试: $ sed '2,${d;=}' listJohn Daggett, 341 King Road, Plymouth MA以上命令尝试在删除第2行到最后一行之后,打印当前行号(=),但是事实上并没有执行该命令。
# Append 追加[line-address]a\text# Insert 插入line-address]i\text# Change 行替换 [address]c\text以上三个命令,行替换命令(c)允许地址为多个地址,其余两个都只允许单个地址( 注:在ArchLinux上测试表明,追加和插入命令都允许多个地址,sed版本为 GNU sed version 4.2.1) 追加命令是指在匹配的行后面插入文本text;相反地,插入命令是指匹配的行前面插入文本text;最后,行替换命令会将匹配的行替换成文本text。文本text并没有被添加到模式空间,而是直接输出到屏幕,因此后续的命令也不会应用到添加的文本上。注意,即使使用-n参数也无法抑制添加的文本的输出。 我们用实际的例子来简单介绍下这三个命令的用法,依然用最初的文本list:
$ cat listJohn Daggett, 341 King Road, Plymouth MAAlice Ford, 22 East Broadway, Richmond VAOrville Thomas, 11345 Oak Bridge Road, Tulsa OKTerry Kalkas, 402 Lans Road, Beaver Falls PAEric Adams, 20 Post Road, Sudbury MAHubert Sims, 328A Brook Road, Roanoke VAAmy Wilde, 334 Bayshore Pkwy, Mountain View CASal Carpenter, 73 6th Street, Boston MA现在,我们要在第2行后面添加'------':
$ sed '2a\--------------------------------------' listJohn Daggett, 341 King Road, Plymouth MAAlice Ford, 22 East Broadway, Richmond VA--------------------------------------Orville Thomas, 11345 Oak Bridge Road, Tulsa OKTerry Kalkas, 402 Lans Road, Beaver Falls PAEric Adams, 20 Post Road, Sudbury MAHubert Sims, 328A Brook Road, Roanoke VAAmy Wilde, 334 Bayshore Pkwy, Mountain View CASal Carpenter, 73 6th Street, Boston MA或者可以在第3行之前插入:
$ sed '3i\--------------------------------------' listJohn Daggett, 341 King Road, Plymouth MAAlice Ford, 22 East Broadway, Richmond VA--------------------------------------Orville Thomas, 11345 Oak Bridge Road, Tulsa OKTerry Kalkas, 402 Lans Road, Beaver Falls PAEric Adams, 20 Post Road, Sudbury MAHubert Sims, 328A Brook Road, Roanoke VAAmy Wilde, 334 Bayshore Pkwy, Mountain View CASal Carpenter, 73 6th Street, Boston MA我们来测试下文本是否确实没有添加到模式空间,因为模式空间中的内容默认是会打印到屏幕的:
$ sed -n '2a\--------------------------------------' list--------------------------------------通过-n参数来抑制输出后发现插入的内容依然被输出,所以可以判定插入的内容没有被添加到模式空间。 使用行替换命令将第2行到最后一行的内容全部替换成'----':
$ sed '2,$c\--------------------------------------' listJohn Daggett, 341 King Road, Plymouth MA--------------------------------------
[address]p[address]=[address]lp命令用于打印模式空间的内容,例如打印list文件的第一行:
$ sed -n '1p' listJohn Daggett, 341 King Road, Plymouth MAl命令类似p命令,不过会显示控制字符,这个命令和vim的list命令相似,例如:
$ echo "column1 column2 column3^M" | sed -n 'l'column1\tcolumn2\tcolumn3\r$=命令显示当前行行号,例如:
$ sed '=' list1John Daggett, 341 King Road, Plymouth MA2Alice Ford, 22 East Broadway, Richmond VA3Orville Thomas, 11345 Oak Bridge Road, Tulsa OK4Terry Kalkas, 402 Lans Road, Beaver Falls PA5Eric Adams, 20 Post Road, Sudbury MA6Hubert Sims, 328A Brook Road, Roanoke VA7Amy Wilde, 334 Bayshore Pkwy, Mountain View CA8Sal Carpenter, 73 6th Street, Boston MA
[address]y/SET1/SET2/它的作用是在匹配的行上,将SET1中出现的字符替换成SET2中对应位置的字符,例如
1,3y/abc/xyz/
会将1到3行中出现的a替换成x,b替换成y,c替换成z。是不是觉得这个功能很熟悉,其实这一点和tr命令是一样的。可以通过y命令将小写字符替换成大写字符,不过命令比较长: $ echo "hello, world" | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'HELLO, WORLD使用tr命令来转换成大写:
$ echo "hello, world" | tr a-z A-Z HELLO, WORLD
[address]nn命令为将下一行的内容提前读入,并且将之前读入的行(在模式空间中的行)输出到屏幕,然后后续的命令会应用到新读入的行上。因此n命令也会同d命令一样改变sed的控制流程。 书中给出了一个例子来介绍n的用法,假设有这么一个文本:
$ cat text.H1 "On Egypt"Napoleon, pointing to the Pyramids, said to his troops:"Soldiers, forty centuries have their eyes upon you."现在要将.H1后面的空行删除:
$ sed '/.H1/{n;/^$/d}' text.H1 "On Egypt"Napoleon, pointing to the Pyramids, said to his troops:"Soldiers, forty centuries have their eyes upon you."
[line-address]r file[address]w file读命令将指定的文件读取到匹配行之后,并且输出到屏幕,这点类似追加命令(a)。我们以书中的例子来讲解读文件命令。假设有一个文件text:
$ cat textFor service, contact any of the following companies:[Company-list]Thank you.同时我们有一个包含公司名称列表的文件company.list:
$ cat company.list AlliedMayflowerUnited现在我们要将company.list的内容读取到[Company-list]之后:
$ sed '/^\[Company-list]/r company.list' textFor service, contact any of the following companies:[Company-list]AlliedMayflowerUnitedThank you.更好的处理应当是用公司名称命令替换[Company-list],因此我们还需要删除[Company-list]这一行:
$ sed '/^\[Company-list]/r company.list;/^\[Company-list]/d;' textFor service, contact any of the following companies:[Company-list]Thank you.但是结果我们非但没有删除[Company-list],而且company.list的内容也不见了?这是怎么回事呢? 下面是我猜测的过程,读文件的命令会将r空格后面的所有内容都当成文件名,即
company.list;/^\[Company-list]/d;
,然后读取命令的时候发现该文件不存在,但是sed命令读取不存在的文件是不会报错的。所以什么事都没干成。 我们用-e选项将两个命令分开就正常了: $ sed -e '/^\[Company-list]/r company.list' -e '/^\[Company-list]/d;' textFor service, contact any of the following companies:AlliedMayflowerUnitedThank you.写命令将匹配地址的所有行输出到指定的文件中。假设有一个文件内容如下,前半部分是人名,后半部分是区域名称:
$ cat text Adams, Henrietta NortheastBanks, Freda SouthDennis, Jim MidwestGarvey, Bill NortheastJeffries, Jane WestMadison, Sylvia MidwestSommes, Tom South现在我们要将不同区域的人名字写到不同的文件中:
$ sed '/Northeast$/w region.northeast> /South$/w region.south> /Midwest$/w region.midwest> /West$/w region.west' textAdams, Henrietta NortheastBanks, Freda SouthDennis, Jim MidwestGarvey, Bill NortheastJeffries, Jane WestMadison, Sylvia MidwestSommes, Tom South查看输出的文件:
$ cat region.region.midwest region.northeast region.south region.west $ cat region.midwest Dennis, Jim MidwestMadison, Sylvia Midwest我们也可以试试将所有的w命令放到一起用分号分隔,发现会出下面的错误,它把w空格后面的部分当作文件名,这样就验证了上面的猜测:
$ sed '/Northeast$/w region.northeast;/south$/w region.south;' textsed: couldn't open file region.northeast;/south$/w region.south;: No such file or directory
[line-address]q当sed读取到匹配的行之后即退出,不会再读入新的行,并且将当前模式空间的内容输出到屏幕。例如打印前3行内容:
$ sed '3q' listJohn Daggett, 341 King Road, Plymouth MAAlice Ford, 22 East Broadway, Richmond VAOrville Thomas, 11345 Oak Bridge Road, Tulsa OK打印前3行也可以用p命令:
$ sed -n '3p' listJohn Daggett, 341 King Road, Plymouth MAAlice Ford, 22 East Broadway, Richmond VAOrville Thomas, 11345 Oak Bridge Road, Tulsa OK但是对于大文件来说,前者比后者效率更高,因为前者读取到第N行之后就退出了。后者虽然打印了前N行,但是后续的行还是要继续读入,只不会不作处理。 到此为止,sed基础命令的部分就介绍完了。 的内容会在最近几天更新上来,主要会介绍sed高级命令。不过一般情况下,会使用基础命令已经差不多了,高级命令不一定需要去了解。 下面是我整理的一份思维导图(附.xmind文件 ):
转载地址:http://hneym.baihongyu.com/