随笔-124  评论-194  文章-0  trackbacks-0
什么是sed?
sed 是在 UNIX ® 操作系统上运行的一个非交互式上下文编辑器。sed 被设计在下列三种情况下发挥作用:
1) 编辑那些对舒适的交互式编辑而言太大的文件。
2) 在编辑命令太复杂而难于在交互模式下键入的时候编辑任何大小的文件。
3) 要在对输入的一趟扫描中有效的进行多个‘全局’编辑函数。
因为每次只把输入的某些行驻留在内存中,并且不使用临时文件,所以可编辑的文件的有效大小,只受限于输入和输出要同时共存于次级存储的要求。
可以单独的建立复杂的编辑脚本并作为给 sed 的命令文件。对于复杂的编辑,这节省了可观的键入和随之而来的错误。从命令文件运行 sed 高效于作者所知道的任何交互式编辑器,甚至包括能用预先写好的脚本驱动的编辑器。

sed是如何操作的?
sed 缺省的把标准输入复制到标准输出,再把每行写到输出之前可能在其上进行一个或多个编辑命令。这种行为可以通过命令行上的标志来更改。
sed编辑命令的一般格式为:
    [address]command [parameter]

一个或两个[address]是可以省略的;可以用任何数目的空白或 tab 把地址和函数分隔开。sed函数必须出现。依据给出的是哪个函数,参数可能是必需的或是可选的。忽略在这些行开始处的 tab 字符和空格。

sed如何对文件的行操作的步骤?
1)sed对[address]里匹配的行,执行命令并输出在stdout。
2)sed在对匹配的行的执行完所有命令后,自动跳到下一个匹配行重复命令执行。
3)sed对文本行的操作,并不会更改文本的行内容。sed是从文本中调出行内容,并备份,然后在这备份上执行sed命令,最终在stdout上输出操作后的行。


sed命令行格式和标志:
sed命令格式:sed [option] 'sed-script' file
在命令行上option:
    * -n:告诉 sed 不复制所有的行,只复制 p 函数或在 s 函数后 p 标志所指定的行。
    * -e:告诉 sed 把下一个参数接受为编辑命令。
    * -f:告诉 sed 把下一个参数接受为文件名;这个文件应当包含一行一个的编辑命令。

什么是模式空间?
模式匹配的范围叫做模式空间。一般而言,模式空间是输入文本中某一行,但是可以通过使用 N 命令把多于一行读入模式空间

sed如何从输入文件里选择编辑的行?
编辑命令要应用于其上的,输入文件中的行可以通过地址来选择。地址可以是行号或者是上下文地址。

通过用花括号(‘{ }’)组合(group)命令,可以用一个地址(或地址对)来控制一组命令的应用。

sed的行号是十进制整数。在从输入读入每一行的时候,增加一个行号计数器;行号地址匹配(选择)导致这个内部计数器等于地址行号的输入行。计数器在多个输入文件上累计运行,在打开一个新文件的时候它不被复零(reset)。

作为特殊情况,字符 $ 匹配输入文件的最后一行。

上下文地址是包围在斜杠中(‘/’)的模式(‘正则表达式’)。sed 识别的正则表达式被构造如下:
   * 1) 普通字符(不是下面讨论的某个字符)是一个正则表达式,并且匹配这个字符。
    * 2) 在正则表达式开始处的‘^’符号(circumflex)匹配在行开始处的空(null)字符。
    * 3) 在正则表达式结束处的美元符号‘$’匹配在行结束处的空字符。
    * 4) 字符‘"n’匹配内嵌的换行字符,而不是在模式空间结束处的换行。
    * 5) 点‘.’匹配除了模式空间的终止换行之外的任何字符。
    * 6) 跟随着星号‘*’的正则表达式,匹配它所跟丛的正则表达式的任何数目(包括 0)的毗连出现。
    * 7) 在方括号‘[ ]’内的字符串,匹配在字符串内的任何字符,而非其他。但是如果这个字符串的第一个字符是‘^’符号,正则表达式匹配除了在这个字符串内的字符和模式空间的终止换行之外的任何字符。
    * 8) 正则表达式的串联(concatenation)是正则表达式,它匹配这个正则表达式的成员所匹配的字符串的串联。
    * 9) 在顺序的‘"(’和‘")’之间的正则表达式,在效果上等同于没有它修饰的正则表达式,但它有个副作用,将在下面的 s 命令和紧后面的规定 10 中描述。
    * 10) 表达式‘"d’意味着与在同一个表达式中先前的‘"(’和‘")’中包围的表达式所匹配的那些字符同样的字符串。这里的 d 是一个单一的数字;指定的字符串是‘"(’的从左至右的第 d 个出现所起始的字符串。例如,表达式‘^"(.*")"1’匹配开始于同一个字符串的两次重复出现的行。
    * 11) 孤立的空正则表达式(就是‘//’)等价于编译的最后一个正则表达式。

注意:要使用正则表达式的元字符(^ $ . * [ ] " /)中的某一个字符作为文字(去匹配输入中它们自身的出现),要对这个特殊字符前导一个反斜杠‘"’。

有的sed命令可能有 0,1 或 2 个地址。在每个命令中都给出了允许的地址的最大数目。地址多于最大允许个数的命令被认为是错误的。

如果命令没有地址,它应用于输入中每个行。
如果命令有一个地址,它应用于匹配这个地址的所有行。
如果命令有两个地址,它应用于匹配第一个地址的第一行,和直到(并包括)匹配第二个地址的第一个后续行的所有后续行。接着在后续的行上再次尝试匹配第一个地址,并重复这个处理。两个地址用逗号分隔。
例子:
    /an/         匹配我们样例文本的第 1, 3, 4 行
    /an.*an/     匹配第 1 行
    /^an/        没有匹配行
    /./          匹配所有行
    /"./         匹配第 5 行
    /r*an/       匹配第 1,3, 4 行(number = zero!)
    /"(an").*"1/ 匹配第 1 行


sed的命令格式:
[address1]command

[address1],[address2]command

前者表示sed对匹配地址的行进行操作
后者表示sed对从匹配地址1的行到匹配地址2的行之间(包括地址1和地址2行)所有的行进行操作

另外sed命令还可以用大括号进行分组,使其作用于同一个地址:
[address]{
    command1
    command2
    command3
}


[address]{command1;command2;command3}

注意:sed的[address]是正则表达式,并且要用/ /限定范围,如:
/^$/,/^ */d

如果sed命令之间用;分隔,可以将多个命令写在同一行,如:n;d;s/sdfd//g

sed脚本的注释行第一个字符必须是"#"号,如:
#wstar.scd:xxxx

sed的命令函数:

sed命令集由25个命令组成,而且sed的命令大多是用单个字符表示。

n(读取下一行)
[address]n

读取[address]匹配行的下一行
n命令改变了正常的流控制,导致输入的下一行取代模式空间中的当前行,如:
/^".H1/{n;/^$/d}      #将匹配匹配^".H1的下一空行删除

=(打印行号)
[address]=

在stdout,打印匹配的行号,如:
/   if/{
=;p
}

p(打印行):
/address/p

在stdout,打印匹配的行

d(删除):
[address]d

如果模行匹配address,那么就删除整个这一行
[address1],[address2]d
删除匹配address1和address2中间的所有行
注意:不允许在被删除的行上做进一步的操作
例子:
/^".sp/d
/^&/,/^".bp/d

a(追加新行):
[address]a"
text

在匹配address的行后追加新的text行。
注意:必须是a命令后跟一个"用于转义第一个行尾,text必须从下一行开始。
例子:
/<larry's address>/a"
4700 Cross Court"
Freach tjck,IN

i(插入新行):
[address]ni"
text

表示在匹配address的行前插入新的text行。

ni"
text

表示在第n行插入新的text行

注意:必须是i命令后跟一个"用于转义第一个行尾,text必须从下一行开始。
例子:
/<larry's address>/i"
4700 Cross Court

2i"
.so macros"
.ds CH first draft

q(退出命令):
[address]q

退出命令q会使sed停止读取新的输入行(并停止将他们发送到输出)
/^".".$/q

nq
向stdout输出1到(n-1)行的内容,到第n行时退出sed
sed '100q' test

c(行更改):
[address]c"
text


[address1],[address2]c"
text

注意:必须是i命令后跟一个"用于转义第一个行尾,text必须从下一行开始
c命令删除当前行并且在该位置放置所提供的文本。当想要匹配行并整个取代它时使用c命令。
/^".sp/c"
.sp .5

/^From/,/^$/{
s/^From//p
c"
<Mail Header Removed>
}

y(转换):
转换语法:y/charators/change-charactors/

y的转换是根据字符的位置来进行的,//里用的不是正则表达式,只是一般的字符序列。它没有词的概念,只是简单的将对应位置上的字符作替换。
注意:charactors和change-charactors的字符数目要一致
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/

s(替换):
替换命令语法为[address]s/pattern/replacement/flags

修饰替换标志flags是:
n     1-512之间的一个数字,表示在模式空间里对匹配pattern的字符串第n次出现后开始进行替换,将替换后的行输出stdout
g     对模式空间里匹配pattern的字符串进行全局替换,将替换后的行输出stdout
p     将匹配行的内容替换后输出stdout
w file 将模式空间内容写到文件file中
替换命令s应用于与[address]匹配的行。如果没有指定行,那么就应用于与pattern匹配的所有行。

在repalcement里可以利用&符号,表示在替换字符串中引用pattern整个匹配内容,如:
s/See Section [1-9][0-9]*".[1-9][0-9]*/(&)/         #给匹配的字符串添加小括号()

sed的控制流函数:
!(非匹配)
[address]!command   #在不匹配的行,执行sed命令

[address1],[address2]!command
非命令导致(写在同一行上的)下一个命令,应用到所有的且只能是未被地址部分选择到那些输入行上。
/^$/!d
#删除不是空行的所有行

{(命令集)
    组合命令‘{’导致下一组命令作为一个块而被应用(或不应用)到组合命令的地址所选择的输入行上。在组合控制下的的命令中的第一个命令可以出现在与‘{’相同的一行或下一行上。
    组合的命令由自己独立在一行之上的相匹配的‘}’终止。
    组合可以嵌套。

:label(标签)
标签是任意不多于7个字符的序列。标签本身占据一行并以冒号开始:,如:
:mylabel
标签将被分支b和测试t命令调用,改变sed控制流。
b mylabel
注意不要在标签后插入空格

b(分支)
branch命令用于脚本将控制权转移别处。

[address]b <label>
label是可选的,如果没有给出label,sed对该行的控制流就会自动转移到结尾处。如果有label就继续执行标签后的行。
例子:
/^".ES/,/^".EE/b
s/^"/''/
s/^$/''/
...
s/@DQ@/"/g

:top
command1
commnd2
/pattern/b top
command3
#模式pattern不匹配时,sed执行command1,command2,command3
#模式pattern匹配时,控制流会跳转到标签top处,sed执行command1,command2,command1,command2,command3

command1
/pattern/b end
command2
:end
command3
#模式pattern不匹配时,sed执行command1,command2,command3
#模式pattern匹配时,sed执行command1,command3

t(测试是否成功替换)
test命令用于判断当前匹配的地址上是否进行了成功替换?成功替换sed的控制流就转到标签。

[address]t <label>
label是可选的,如果没有给出label,sed对该行的控制流就会自动转移到结尾处。如果有label就继续
例子:
/".Rh 0/{
s/""(.*")"/"2,"3/g
t break
s/""(.*")"/"1,"2/g
t break
...
break:
more commands
}
#若该行有替换,则直接跳到break标签处执行下边命令


匹配多行:

假设我们的目标文件test内容是这样的:

file content
aabbcc<<<comment part 1
comment part 2>>>
ddeeff

现在需要把<<<...>>>这一段替换为“COMMENT”,那么sed语法应当是:

:begin
/<<</,/>>>/ {
/>>>/! {
$! {
N;
b begin
}
}
s/<<<.*>>>/COMMENT/;
}

上述语句存储在test.sed中,那么执行的方式和结果就是:

$ sed -f test.sed test
file content
aabbccCOMMENT
ddeeff

把正则直接写到命令里面也可以,用“;”来分隔命令即可:

$ sed -e ":begin; /<<</,/>>>/ { />>>/! { $! { N; b begin }; }; s/<<<.*>>>/COMMENT/; };" test
file content
aabbccCOMMENT
ddeeff

注意右花括号之后也要加上分号“;”,如果再加上-i参数就可以直接把改动写到原文件中去了。

怎么样?看懂了么?我来详细说明吧,看那个多行命令的test.sed文件的内容:

  • 首先花括号{}代表命令块的开始,类似c的语法,后面就不再说了。

  • :begin,这是一个标号,man中叫做label,也就是跳转标记,供b和t命令用,本例中使用了b命令。

  • /<<</,/>>>/,这是一个地址范围(Addresses),后面 {}中的命令只对地址范围之间的内容使用。其中逗号前面的部分是开始地址,逗号后面是结束地址,都是正则表达式。由于sed是“流”式“行”处理,所以结 束地址是可以省略的,即如果地址的结束范围不存在,那么将一直处理到文件结尾。本例中使用这个地址范围主要是缩小处理的数据量,因为虽然后面用N命令把对 一行的处理扩展为了多行,但如果从文件开头一直N扩展到<<<出现为止,buffer中要处理的字符串可能会很长,影响效率。所以去掉 这个处理范围也是能够得到正确结果的,比如:

    $ sed -e ":begin; { />>>/! { $! { N; b begin }; }; s/<<<.*>>>/COMMENT/; };" test
    or
    $ sed -e "{:begin; />>>/! { $! { N; b begin }; }; s/<<<.*>>>/COMMENT/; };" test
  • />>>/!>>>是要替换内容的结束标记,带上!就是说当一行处理完毕之后,如果没有发现结束标记。。。

  • $!$在正则中表示字符串结尾,在sed中代表文件的最后一行,本句和上一句结合起来的意思就是:如果在本行没有发现结束标记,并且当前扫描过的行并不是文件的最后一行。

  • N;,把下一行的内容追加(append)到缓冲区(pattern)之后,在我们的例子中,在处理aabbcc<<<comment part 1这一行的内容时,就会执行到这里,然后把下一行的内容comment part 2>>>一起放入缓冲区,相当于“合并”成了一行(sed的缓冲区中默认都只会包含一行的内容)。

  • b begin,由于仍然没有找到结束标记<<<(注意上一条说的缓冲区还没有被处理),所以在这里跳回到标号begin,重新开始命令。如果开始和结束标记之间间隔了多行,那么就会有多次跳转发生。

  • s/<<<.*>>>/COMMENT/;,终于,/>>>/!不再匹配成功,也就是我们已经找到了结束标记,那么用s命令来进行替换。如果开始和结束标记在一行的话,就会越过上面那些复杂的处理,直接执行到这里了。

转自:
http://www.fwolf.com/blog/post/346
http://hi.baidu.com/hellolinuxworld/blog/item/5e3aa7080e6350c63bc76309.html


posted on 2009-09-01 10:12 我爱佳娃 阅读(3956) 评论(1)  编辑  收藏 所属分类: 工具使用

评论:
# re: SED最佳参考[未登录] 2009-11-23 20:02 | fly
找了很久,这篇非常好。感谢分享啊。。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: