网络推荐



本广告位招租!

推荐给好友 上一篇 | 下一篇

sed 实例讲解(中篇)

{(VRAd1Fsed 是很有用(但常被遗忘)的 UNIX 流编辑器。在以批处理方式编辑文件或以有效方式创建 shell 脚本来修改现有文件方面,它是十分理想的工具。BSD爱好者乐园6i4`+WC ]:z/{

BSD爱好者乐园 W4R_+R0o

替换!
1J3q,M wBKq:]让我们看一下 sed 最有用的命令之一,替换命令。使用该命令,可以将特定字符串或匹配的规则表达式用另一个字符串替换。下面是该命令最基本用法的示例:

P4^yyTBSD爱好者乐园F/t(Xb|5Y5|.Me

$ sed -e 's/foo/bar/' myfile.txt
3[aP ?:N _上面的命令将 myfile.txt 中每行第一次出现的 'foo'(如果有的话)用字符串 'bar' 替换,然后将该文件内容输出到标准输出。请注意,我说的是每行第一次出现,尽管这通常不是您想要的。在进行字符串替换时,通常想执行全局替换。也就是说, 要替换每行中的所有出现,如下所示: 

M E"PN3\!K:M"HK

D.zvAb#Q$ sed -e 's/foo/bar/g' myfile.txt 
9[3d'k|I;mRDi在最后一个斜杠之后附加的 'g' 选项告诉 sed 执行全局替换。BSD爱好者乐园 l5f\'T]'_

#I-Io(sy)h1A*i6|,g关于 's///' 替换命令,还有其它几件要了解的事。首先,它是一个命令,并且只是一个命令,在所有上例中都没有指定地址。这意味着,'s///' 还可以与地址一起使用来控制要将命令应用到哪些行,如下所示:

-|,P5jc,\%J5t v"pBSD爱好者乐园)S;o2P L2o+r$W+O

$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt 
Wc%cX\8Y上例将导致用短语 'entrapment' 替换所有出现的短语 'enchantment',但是只在第一到第十行(包括这两行)上这样做。BSD爱好者乐园_7lbPg'G FE:].xj

etAg\1u*\.K"v$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt BSD爱好者乐园b7o4olv4e9P.O
该例将用 'mountains' 替换 'hills',但是,只从空行开始,到以三个字符 'END' 开始的行结束(包括这两行)的文本块上这样做。BSD爱好者乐园uu _X9u~

9cT7C4M)b6mt n:|关于 's///' 命令的另一个妙处是 '/' 分隔符有许多替换选项。如果正在执行字符串替换,并且规则表达式或替换字符串中有许多斜杠,则可以通过在 's' 之后指定一个不同的字符来更改分隔符。例如,下例将把所有出现的 /usr/local 替换成 /usr:BSD爱好者乐园iyN,E_L.m2H@

6bsC QwX;i^$ sed -e 's:/usr/local:/usr:g' mylist.txt BSD爱好者乐园1U1t5O`C/G%e
在该例中,使用冒号作为分隔符。如果需要在规则表达式中指定分隔符字符,可以在它前面加入反斜杠。BSD爱好者乐园6Y7}mN0~8]s)} ~

,D!f"e}o4]*]I规则表达式混乱BSD爱好者乐园I{z$glJ+i V
目前为止,我们只执行了简单的字符串替换。虽然这很方便,但是我们还可以匹配规则表达式。例如,以下 sed 命令将匹配从 '<' 开始、到 '>' 结束、并且在其中包含任意数量字符的短语。下例将删除该短语(用空字符串替换):BSD爱好者乐园-`1NWHAd]S

BSD爱好者乐园 wmA9g1A.d

$ sed -e 's/<.*>//g' myfile.html BSD爱好者乐园-AK7{c;O^
这是要从文件除去 HTML 标记的第一个很好的 sed 脚本尝试,但是由于规则表达式的特有规则,它不会很好地工作。原因何在?当 sed 试图在行中匹配规则表达式时,它要在行中查找最长的匹配。在我的前一篇 sed 文章中,这不成问题,因为我们使用的是 'd' 和 'p' 命令,这些命令总要删除或打印整行。但是,在使用 's///' 命令时,确实有很大不同,因为规则表达式匹配的整个部分将被目标字符串替换,或者,在本例中,被删除。这意味着,上例将把下行:BSD爱好者乐园3?@%_:?L4y9iZY }

8e)}Z ?9Dwu<b>This</b> is what <b>I</b> meant. BSD爱好者乐园5NH0I)D%b^Sf_#OC
变成:

n Y/M!o)[.Cf1@iLBSD爱好者乐园^)k.v m3\9B5?3{

 meant. 
/P}F"G9ON(x:V8}我们要的不是这个,而是:

*_{uR nT`+M]BSD爱好者乐园POA6z*y

 This is what I meant. BSD爱好者乐园qP^ k"}e j5{
幸运的是,有一种简便方法来纠正该问题。我们不输入“'<' 字符后面跟有一些字符并以 '>' 字符结束”的规则表达式,而只需输入一个“'<' 字符后面跟有任意数量非 '>' 字符并以 '>' 字符结束”的规则表达式。这将与最短、而不是最长的可能性匹配。新命令如下:

H]rX*IA4sBSD爱好者乐园*\x KMOsWO,w7@

 $ sed -e 's/<[^>]*>//g' myfile.html BSD爱好者乐园M KZn$yc|A4L5_
在上例中,'[^>]' 指定“非 '>'”字符,其后的 '*' 完成该表达式以表示“零或多个非 '>' 字符”。对几个 html 文件测试该命令,将它们管道输出到 "more",然后仔细查看其结果。

3] y Ao,e8Gz3O#eBSD爱好者乐园E%ir:AT*k8o&ff0q

更多字符匹配
uJ#flU3V'[ ]' 规则表达式语法还有一些附加选项。要指定字符范围,只要字符不在第一个或最后一个位置,就可以使用 '-',如下所示:BSD爱好者乐园OB)iqZ;KA!^"Q

BSD爱好者乐园.OY[6eLN3e&XfD

 '[a-x]*' 
-v A}dx2}v这将匹配零或多个全部为 'a'、'b'、'c'...'v'、'w'、'x' 的字符。另外,可以使用 '[]' 字符类来匹配空格。以下是可用字符类的相当完整的列表:BSD爱好者乐园J[Wz V{

/n[1n7a0S2L?Z2u字符类   描述 BSD爱好者乐园%l5zcp5y u"O+o]
[:alnum:] 字母数字 [a-z A-Z 0-9]BSD爱好者乐园_}6d^tf
[:alpha:] 字母 [a-z A-Z]BSD爱好者乐园4BO6M_[ J9ly
[:blank:] 空格或制表键
M2bU{A-R%yDv0iiX[:cntrl:] 任何控制字符
vjTd(U[:digit:] 数字 [0-9]BSD爱好者乐园1`N[e^YB?+~
[:graph:] 任何可视字符(无空格)
iyjc&F"y9n wfH[:lower:] 小写 [a-z]
6?I!s/wvkK\M[:print:] 非控制字符BSD爱好者乐园 Bozl(UI+p)m
[:punct:] 标点字符BSD爱好者乐园)P8@s&D ?t KUK
[:space:] 空格
fO6P\:Grg[:upper:] 大写 [A-Z]
o9_o _7]Ft$ez[:xdigit:] 十六进制数字 [0-9 a-f A-F]
T0K @9Ncz| BSD爱好者乐园}i4q2k q }
尽可能使用字符类是很有利的,因为它们可以更好地适应非英语 locale(包括某些必需的重音字符等等).BSD爱好者乐园1r Q*c~7I&v!nA

BSD爱好者乐园)dnX.@"Y:}hD

高级替换功能
9C$nS[:~Y"_&n!D我们已经看到如何执行简单甚至有些复杂的直接替换,但是 sed 还可以做更多的事。实际上可以引用匹配规则表达式的部分或全部,并使用这些部分来构造替换字符串。作为示例,假设您正在回复一条消息。下例将在每一行前面加上短语 "ralph said: ":BSD爱好者乐园}S%HCN1z

x6tLu W!E5]9r $ sed -e 's/.*/ralph said: &/' origmsg.txt BSD爱好者乐园-r2M-y#GC5zE
输出如下: BSD爱好者乐园t1`],R'O]n

9`i,i,I:BB)a d ralph said: Hiya Jim, ralph said: ralph said: BSD爱好者乐园I4y0s(V6vz`GxI
 I sure like this sed stuff! ralph said: BSD爱好者乐园8\S&s!XHomCl3d
该例的替换字符串中使用了 '&' 字符,该字符告诉 sed 插入整个匹配的规则表达式。因此,可以将与 '.*' 匹配的任何内容(行中的零或多个字符的最大组或整行)插入到替换字符串中的任何位置,甚至多次插入。这非常好,但 sed 甚至更强大。BSD爱好者乐园V_jc1beR2{&X7L

BSD爱好者乐园9Lp:_Kec3A/B2O

那些极好的带反斜杠的圆括号
'm,s3n}#gju"Iy+o's///' 命令甚至比 '&' 更好,它允许我们在规则表达式中定义区域,然后可以在替换字符串中引用这些特定区域。作为示例,假设有一个包含以下文本的文件:BSD爱好者乐园%KA w!M4A/Q ~&b!`

BSD爱好者乐园z#s5W]'pD&z

 foo bar oni eeny meeny miny larry curly moe jimmy the weasel 
V1L!m9]3L现在假设要编写一个 sed 脚本,该脚本将把 "eeny meeny miny" 替换成 "Victor eeny-meeny Von miny" 等等。要这样做,首先要编写一个由空格分隔并与三个字符串匹配的规则表达式。BSD爱好者乐园.U%B7z`^'}$`

BSD爱好者乐园3bNH@k#_K

 '.* .* .*' BSD爱好者乐园v7TNVc1g6@8u5h*\ `
现在,将在其中每个感兴趣的区域两边插入带反斜杠的圆括号来定义区域:BSD爱好者乐园Wn7]/yZ|n Av

:c4m!iVh.pX '\(.*\) \(.*\) \(.*\)' 
D-X b.e5| H5~ H除了要定义三个可在替换字符串中引用的逻辑区域以外,该规则表达式的工作原理将与第一个规则表达式相同。下面是最终脚本:

H/`r8w fKh~#OBSD爱好者乐园Cw F!A-c

 $ sed -e 's/\(.*\) \(.*\) \(.*\)/Victor \1-\2 Von \3/' myfile.txt 
s7|OF+pZ*N/Wa如您所见,通过输入 '\x'(其中,x 是从 1 开始的区域号)来引用每个由圆括号定界的区域。输入如下:BSD爱好者乐园,jia7AU-n

BSD爱好者乐园B'D1AZgZ-v8m Zp

 Victor foo-bar Von oni Victor eeny-meeny Von miny Victor larry-curly Von moe Victor jimmy-the Von weasel 
W!`A,kg#zG)R随着对 sed 越来越熟悉,您可以花最小力气来进行相当强大的文本处理。您可能想如何使用熟悉的脚本语言来处理这种问题 -- 能用一行代码轻易实现这样的解决方案吗?BSD爱好者乐园X l7UB}/EK c;X

u7O d f[d#P AJ!Bk组合使用BSD爱好者乐园\^(Irk9be
在开始创建更复杂的 sed 脚本时,需要有输入多个命令的能力。有几种方法这样做。首先,可以在命令之间使用分号。例如,以下命令系列使用 '=' 命令和 'p' 命令,'=' 命令告诉 sed 打印行号,'p' 命令明确告诉 sed 打印该行(因为处于 '-n' 模式)。

r_[7~c.I BBSD爱好者乐园,B[+ws&a6gA"] m

 $ sed -n -e '=;p' myfile.txt BSD爱好者乐园*\Q~r8NajF
无论什么时候指定了两个或更多命令,都按顺序将每个命令应用到文件的每一 行。在上例中,首先将 '=' 命令应用到第 1 行,然后应用 'p' 命令。接着,sed 继续处理第 2 行,并重复该过程。虽然分号很方便,但是在某些场合下,它不能正常工作。另一种替换方法是使用两个 -e 选项来指定两个不同的命令:

fX6n)ydKU*Ka'HJ7l1gBSD爱好者乐园#U]C:q s&hX^^

 $ sed -n -e '=' -e 'p' myfile.txt BSD爱好者乐园LD G6iC!a5@
然而,在使用更为复杂的附加和插入命令时,甚至多个 '-e' 选项也不能帮我们的忙。对于复杂的多行脚本,最好的方法是将命令放入一个单独的文件中。然后,用 -f 选项引用该脚本文件:

/Q)| p4y0N'X.@HBSD爱好者乐园:@ i;nKg

 $ sed -n -f mycommands.sed myfile.txt BSD爱好者乐园AV(Vr R g.l;d
这种方法虽然可能不太方便,但总是管用。BSD爱好者乐园"O!U)e gS BRm9nR

BSD爱好者乐园#saF"G0|p3R)R#_

一个地址的多个命令BSD爱好者乐园.k X(eYj:n3IHa
有时,可能要指定应用到一个地址的多个命令。这在执行许多 's///' 以变换源文件中的字和语法时特别方便。要对一个地址执行多个命令,可在文件中输入 sed 命令,然后使用 '{ }' 字符将这些命令分组,如下所示:BSD爱好者乐园7m+|R|RA)}

BSD爱好者乐园m sL$C"Q

 1,20{  s/[Ll]inux/GNU\/Linux/g  s/samba/Samba/g  s/posix/POSIX/g } BSD爱好者乐园E,w` ~)X1d

BSD爱好者乐园9N"j/g*po~m

上例将把三个替换命令应用到第 1 行到第 20 行(包括这两行)。还可以使用规则表达式地址或者二者的组合:

7G8uH vT3KBSD爱好者乐园9H8K o2KKP3w!i

 1,/^END/{         s/[Ll]inux/GNU\/Linux/g         s/samba/Samba/g         s/posix/POSIX/g  p } 

e&F/S@j P,f}"[BSD爱好者乐园 Tk^F;{ sQ

该例将把 '{ }' 之间的所有命令应用到从第 1 行开始,到以字母 "END" 开始的行结束(如果在源文件中没发现 "END",则到文件结束)的所有行。

b'B.@bub B{lBSD爱好者乐园 pm ]W"p/tY

附加、插入和更改行BSD爱好者乐园}X;m0p,t(QS jt
既然在单独的文件中编写 sed 脚本,我们可以利用附加、插入和更改行命令。这些命令将在当前行之后插入一行,在当前行之前插入一行,或者替换模式空间中的当前行。它们也可以用来将多行插入到输出。插入行命令用法如下:

/Nz:t w V cBSD爱好者乐园G9[ Ji;VG n"LD-?

 i\ This line will be inserted before each line 

,y)mm1fj(Z

d9o:x,p~%m&q如果不为该命令指定地址,那么它将应用到每一行,并产生如下的输出:

5ad4?^ N3XBSD爱好者乐园U\@(aB+i7_v*h

 This line will be inserted before each line line 1 here 
.x"_6i? P_6F'O This line will be inserted before each line line 2 here 
*m.M P8C$f U!x n8R This line will be inserted before each line line 3 here 
[V)Cl:Sr D6wd This line will be inserted before each line line 4 here 

#N&I|s_6H.A MBSD爱好者乐园 r&b{;{B@;gv0j9~

如果要在当前行之前插入多行,可以通过在前一行之后附加一个反斜杠来添加附加行,如下所示:

+@I.j!}3GXY)c

[M$^qq i\ insert this line\ and this one\ and this one\ and, uh, this one too.BSD爱好者乐园~\V!}6E$k\$qF
 BSD爱好者乐园G](MP7d!iUEJX
附加命令的用法与之类似,但是它将把一行或多行插入到模式空间中的当前行之后。其用法如下:

2HT7A9EpsW#Hc

6gTjY;b9v9t a\ insert this line after each line.  Thanks! :) BSD爱好者乐园ln&_.qw2~6N(g
另一方面,“更改行”命令将实际替换模式空间中的当前行,其用法如下:

Qe#Ri1v$Bh9d-z

,j&l+E$JibH c\ You're history, original line! Muhahaha! 

xp#Z_#FlC因为附加、插入和更改行命令需要在多行输入,所以将把它们输入到一个文本 sed 脚本中,然后通过使用 '-f' 选项告诉 sed 执行它们。使用其它方法将命令传递给 sed 会出现问题。
[重要提醒]对本篇资料有疑问,请到论坛讨论,尽量使文章准确无误>>>
[版权声明]BSD爱好者乐园站内文章,如来源不是互联网,则均系原创或翻译之作,可随意转载,或以此为基础进行演译,但务必以链接形式注明原始出处和作者信息,否则属于侵权行为。另对本站转载他处文章,俱有说明,如有侵权请联系本人,本人将会在第一时间删除侵权文章。
TAG: sed shell
 

评分:0

我来说两句

seccode