Chinaunix首页 | 论坛 | 博客
  • 博客访问: 346381
  • 博文数量: 49
  • 博客积分: 817
  • 博客等级: 军士长
  • 技术积分: 496
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-16 19:05
文章分类

全部博文(49)

文章存档

2014年(6)

2013年(7)

2012年(8)

2011年(28)

分类: LINUX

2011-10-21 15:10:06

sed适用于按行处理的数据,awk适用于按列、字段处理的数据;
语法:sed [options] '{command}' [filename]

==================================================================================
★ pattern space & hold space
** pattern space 称为模式空间;
** hold space 称为保留空间或者缓冲区,初始为空;
1)模式空间用来储存与模式相匹配的那一行的内容,一旦sed 完成对模式空间中行的处理,其中的内容就会输出到屏幕,然后删除模式空间的内容,读取下一条与模式相匹配的行放入模式空间
2)保留空间是sed用来临时存储一些数据,方便sed做更复杂的动作;常用的空间参数有:
a\, c\, i\, b, dD, gG, hH, nN, p, q, r, s/pattern/repalcement/, t, w, x, y/pattern1/pattern2/ 等。会阻止余下命令执行的参数有q, d, D;执行n时,会先输出模式空间中的内容,然后清空模式空间内容,接着读入下一行数据;

1. q[exit_code]
只支持单个地址范围,例如:
sed 10q # 相当于 head -n 10
sed -n -e '5q;p' # 将打印 4 行,到第 5 行时,满足 sed 的q条件,将退出sed,由于 p 没有任何范围,i.e. p 没有任何条件限制,因此会一直执行
sed -n -e '5q;p' | echo "haha" # 打印 0-4 行,并在最后打印 haha
sed -n -e '5q1;p' && echo "haha" # 打印 0-4 行,不会输出 haha,这是因为 sed 返回码为 1,&& 操作不允许执行 echo 了 

2. d
如果该命令被执行,会导致清空 pattern space,并且停止执行 sed 的命令,返回到 sed ,sed将根据当前处理的行号读入下一行,重新开始执行 sed 的命令;
如果 d 命令被执行,那么 d 后面的所有命令都不会被执行,例如 sed -e 'd' -e 'p' ,这里的 p 是不会被执行的

sed -e '/./{H;$!d;}' -e 'x;/main/!d;' main.c
# 查找包含 main 的段,段落之间以空行分割
# H命令的过程是把 hold space 中增加一个 newline,而后将 pattern space 中的内容拷贝到 hold space中。
# 第一个 -e ,指出如果这行不是空行,则将这行的内容拷贝到hold space中,并删除这行,由于执行了 d 命令,那么第二个 e 就不会被执行,并且 pattern space 中的内容已经被清空,执行了 d 命令后,控制权交给了 sed,由于没有指定 -n ,sed 输出pattern spacese,由于执行了 d 后pattern space 什么也没有,所以什么也没有输出,sed 判断是否已经读完了档,如果读完了,就退出sed,如果未读完,则继续读入下一行,读入的内容将去掉结尾的newline符号\n;
   如果该行是空行,则不会执行第一个 -e ,由第二个 -e 来处理,x 命令将 hold space 和 pattern space 中的内容进行交换, pattern space中存储的就是刚读入的段落, hold space 中存储的就是空行,也就是说 hold space 中什么也没有,相当于清空 hold space,而后查找使用/main/查找pattern space 中是否包含 main,如果不包含则执行 d 命令, 如果包含 main,则不执行 d 命令,而后退出 sed命令, sed 在自动读入下一行前,由于没有 -n,那么会将 pattern space 中内容输出到 stdout,而后在读入下一行。

sed '/./,$!d' # 删除文件开头部分中的所有空白行,i.e. 从有字符开始的行直到最后一行保留,其他删除

3. D
删除模式空间中的第一行,如果删除后,模式空间中还有剩余行,则不读入下一行内容,返回 D 之前的命令,重新执行;如果 D 后,patten space 中没有任何内容,则将退出 sed命令,将控制权交给 sed;
在没有 -n 参数时,sed 会输出 pattern space 中的内容,如果最后一行没有被读入,则 sed 继续读入下一行,并去掉结尾的 \n 放到 pattern space 中,继续执行 sed 命令,如果最一行已经读入,则退出 sed

D 命令会引起循环,如果 D 被执行,那么 D 后面的命令是不会被执行的
例如 literal 檔是
a
b
c

sed  -n -e 'p' -e '$!N;=;$p;D;p'  literals
# 1. D 后的命令 p 不会被执行
处理结果是
a # line 1
2 # line 2
b # line 3
3 # line 4
b # line 5
c # line 6
c # line 7
3 # line 8
c # line 9
下面一点一点解释 sed  -n -e 'p' -e '$!N;=;$p;D;p'  literals :
line 1: 这个 a 是由第一个 p 打印出来的,这时的patter space 是 [a]
line 2: 2 是由 = 打印出来的,打印 2 是因为 $!N 读入了下一行,这是 pattern space 是 [a\nb]
          由于当前是第 2 行,$p 不会有输出;而后执行 D ,删除后 pattern space 是 [b],
          由于有剩余,因此会从第一个 -e 'p' 处执行,也就是说 D 命令会产生循环。
          D后的p不会被执行
line 3:  这个 b 是由第一个 p 打印出来的,证明了前次执行完 D 命令后的patter space 是 [b],与           在 line 2 的 D 后没有变换
line 4:  3 是由 = 打印出来的,打印 3 是因为 $!N 读入了下一行,这时 pattern space 是                   [b\nc],可以在下一行解释证明这一点
line 5,6: 这两行是由 $p 打印出来的,由于当前是第三行,也就是 $ 行,p 命令会打印模式空间的内            容。而后执行 D ,删除后 pattern space 是 [c],由于有剩余,因此会从第一个 -e 'p'              处执行,也就是说 D 命令会产生循环。D后的p不会被执行
line 7: 这个 c 是有第一个 p 打印出来的,证明了前次执行完 D 命令后的patter space 是 [c]
line 8: 3 是由 = 打印出来的,已经到了 $ ,因此 $!N 不会读入任何内容
line 9: 这个9是由 $p打印出来的,也进一步证明了 $!N 没有执行,而后执行完 D 命令后,
        D 后 pattern space 中没有任何内容,结束本次的处理,把控制权交给了sed。sed 检查当前执         行到了最后一行,也完成了处理,退出 sed 命令,整个过程中, sed 命令没有自动读入一行,         都是由手动的 $!N 读入的,

sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h # 模拟 grep -A1 -B1

sed '$!N; /^\(.*\)\n\1$/!P; D' 
# 删除档中重复的连续的行(似于"uniq"命令)
# 重复行中第一行保留,其他删除
# 这里只需要注意查找重复行的 regex 为 ^\(.*\)\n\1$

sed -e '$!N;$!P;$!D;$d'
# 删除一个档中最后2行
# a: $!N 当前处理的不是最后一行,那么就把下一行读进来,而后当前处理的行号 + 1,
# $!P 如果当前处理的行号是最后一行就不会打印,
# $!D 如果当前处理的行号不是最后一行就删除 pattern space 的的第一行,
# 如果 pattern space 中还有内容,就重新执行命令,转 a,在 D 后,pattern space 中
# 始终会有内容
# $d 如果当前是最后一行,则删除pattern space 中的内容,不用使用 $d,因为 $d 前面的是
# $!D,因此,如果能够执行到 d ,那么一定是 $

# 删除档中除一行空白行之外的所有连续空白行,也同时删除所有从头到尾的所有空白行(似于"cat -s")
sed '/./,/^$/!d' # method 1, allows 0 blanks at top, 1 at EOF
sed '/^$/N;/\n$/D' # method 2, allows 1 blank at top, 0 at EOF

4. p
打印模式空间的内容,在没有 -n 参数时,-e 的任何参数,都将附加 ;p,e.g.:
sed -e '1,10d' myfile 相当于 sed -e '1,10d;p' myfile
在存在 -n 参数时,如果 p 命令的同范围的命令中,在 p 前的命令没有 d 命令,那么只有满足了 p 的范围的内容,才会被打印
sed -n -e '/main/!p/ main.c # 如果行中不包含 main ,则打印这行,如果包含 main 则不打印这行c

4'. P
打印模式空间的第一行
sed -n -e 'h;G;P' main.c # 经过 h;G; 后,模式空间的内容已经是本次读入的行的两行了,但 P 只会打印一行,而 p 会打印两行

5. n
If auto-print is not disabled, print the pattern space, then, regardless, 
replace the pattern space with the next line of input. If there is no more input then 
sed exits without processing any more commands. 
执行到这个命令时,如果没有 -n 选项;输出当前 pattern space 中的内容,无论是否有-n,将读入下一行的内容来替换 pattern space 中的内容
所谓执行到这个命令,是指与 n 同范围的命令中,在 n 的前面没有 d 或 q

sed -n '/regexp/{n;p;}' # 打印在"regexp"之后紧跟那一行,但是不打印包含"regexp"的行.

5'. N
在当前的 pattern space 中添加一个 newline 标识符,并且读入一行 append 到 patten space 中
注意:无论是 sed 自动读到 pattern space 中的内容,还是通过 n N 读入到 pattern space 中的内容,都会去掉结尾的 newline,newline 包括 linux 的 \n 或 windows 的 \r\n
$!N 如果不是最后一行,就读入新行附加于pattern space之后,
Add a newline to the pattern space, then append the next line of input to the pattern space. 
If there is no more input then sed exits without processing any more commands. 
如果N命令没有读入任何内容,sed 就会退出,不会继续处理 N 后的 sed命令
   在 gnu sed 4.1.5 中,如果没有 -n,会输出 pattern space 中的内容,而后在退出 sed
   在 gnu sed 4.1.5 中,可能会直接退出 sed,不会输出 pattern space中 的内容

6. =
执行 = 命令时,不会修改 pattern space,而是直接把 行号+newline 打印到输出中
注意: = 命令操作的不是 pattern space,而是直接把行号打印到到输出
例如: sed -e '=;d' main.c # 打印完行号后,删除 pattern space 中的内容
      sed -e 'd;=' main.c # 不会输出任何内容,因为 = 不会被执行
      sed -n -e '=' main.c # 由于 -n 参数,因此只输出行号,而不是出 pattern space 中的任何内容
      sed -n -e 'p;=;p' main.c # 可以看出 = 没有动过 pattern space,而是直接把 pattern space 中的内容打印到输出中

7. {action_1;action_2;...;action_N}
执行满足范围的一组命令,e.g. 比较 sed -n -e '1,2{=;p} myfile 与 sed -n -e '1,2=;p' myfile 输出内容的不同,要求myfile > 2行;前者会只输出第1、2行,并在每行前加行号;后者在1、2行前加航后,并且会输出余下的所有行;
在{}中可以继续插入条件,例如:
a
b

c
使用 sed -n -e '/./{$d;p}' file,就会过滤掉最后一行,并且不打印空行

8. 替换命令 s/regexp/replacement/flags,s也就是一个命令,e.g.
sed -n -e '1,5{s/while/WHILE/g;p}' myfile # 将 1-5 的while替换成WHILE,并且打印这 5 行
sed -n -e 's/\(v\+\)i/\1/gp' # 如果包含vvi,则认为这是一个输入错误,使用者本意输出的是vv,而非vi,这时可以使用以上命令进行替换,e.g. 录入 vvvvvi,就会显示vvvvv
    如果存在多个元组,则可以在 \$ 来匹配最后一个元组
    如果有元组的话, & 相当于 \0,如果没有的话,& 将匹配所有匹配的内容,可以在/// 的第2个/和第3个/之间 使用 & 来取匹配的文本
    s/abc/cba/中的界定符 '/' 可以使用其它字符代替,只要是跟在 s 后边的,就认为是界定符;如 shabchcbah,其中h就是界定符,这时 h 就是特殊字符,如果在匹配字符串中想使用 h 就得使用反斜线 '\' 转义;
flags的取值
    a. g 整行全部替换,预设为只替换第一个匹配项目
    b. number,只替换第 number 个匹配的内容,e.g. sed -n -e 's/vi/VI/2p' myfile,遇到的第1个vi不替换,只替换第2个vi;
    c. p 打印参数,这个 p 与 p 命令类似,但这个是 s 命令的参数

9. a i c 命令,a\TEXT i\TEXT c\TEXT
    a 命令是在指定的范围内,输出范围内的每一行,并且在每一行后加上换行符,而后输出TEXT
    i 命令与 a 命令类似,但不是 append ,而是 insert
    c 命令与 a 命令相似,但不是 append ,而是 changeTo
        sed -e '1,3c\changeToMyString' myfile
        sed -e '/regex/a\changeToMyString' myfile

======================================================
标签
b label ,无条件跳转到标签label,如果label没有指定,跳转到命令的结尾
t label ,如果最后一次输入的最后一个 s/// 子命令执行成功,跳转到标签label,如果label没有指定,跳转到命令的结尾

例1: 用标签完成 是AA就加上YES,不是AA就加NO

使用t命令:
linux@ubuntu:~$ cat urfile
AA
BC
AA
CB
CC
AA
linux@ubuntu:~$ sed '/^AA/s/$/ YES/;t;s/$/ NO/' urfile
AA YES
BC NO
AA YES
CB NO
CC NO
AA YES
复制代码
如果是AA,执行s/$/ YES/,s命令执行成功,执行t命令,没有标签,跳转到命令的结尾,这样将会跳过后面的s/$/ NO/
如果不是AA,s/$/ YES/不执行,则t命令也不执行,只执行后面的s/$/ NO/

使用b命令:
linux@ubuntu:~$ sed '/^AA/ba;s/$/ NO/;b;:a;s/$/ YES/' urfile
AA YES
BC NO
AA YES
CB NO
CC NO
AA YES
复制代码
如果是AA,执行ba,跳转到标签a处,这样会跳过中间的s/$/ NO/;b,只执行后面的s/$/ YES/
如果不是AA,ba不执行,执行s/$/ NO/,执行b,没有标签,跳转到命令的结尾,这样将会跳过后面的s/$/ YES/

例2: 合并行:
linux@ubuntu:~$ cat urfile
114.113.144.2:
19ms
19ms
19ms
36ms
22ms
19ms
18ms
218.61.204.73:
0ms
0ms
0ms
0ms
0ms
0ms
0ms
linux@ubuntu:~$ sed ':a;$!N;/ms$/s/\n/ /;ta;P;D' urfile
114.113.144.2: 19ms 19ms 19ms 36ms 22ms 19ms 18ms
218.61.204.73: 0ms 0ms 0ms 0ms 0ms 0ms 0ms
复制代码
实现思路:
1、读入下一行数据
2、判断是否以ms结尾
3、如果是,替换\n为空格,跳转到1
4、如果不是,打印本行数据,删除本行数据,跳转到1

代码实现:
:a                #定义标签a
$!N                #不是最后一行,执行N命令
/ms$/s/\n/ /       #如果以ms结尾,将\n替换为空格
ta                #如果s///命令执行成功,跳转到标签a处
P                    #打印pattern space的第一行
D                    #删除pattern space的第一行,循环

此代码是使用sed进行合并行操作的典型代码,对于不同的情况,只需要将/ms$/替换成需要的正则表达式即可,思路上是通用的。

===================================================================================
★ Addresses
  很多情况下,我们需要指定特定的行来进行处理,这时候就用到 addresses 匹配;
1) first~step     以first行为基数,step为步进值,匹配这些行;
      例:sed -n '3~5p' text
      从第3行开始,以5为步进值打印行,即只打印第3、8、13、18……行,直到文件末尾;
2)/regexp/    指定符合 regexp 的行来进行处理;可以使用 I 参数忽略大小写;如 /regexp/I;
   \qreqexpq   用\转义q后,q也能做为界定符来用,用法与上一行一样,也适用于其它字符;实际操作了一下,在 q 后边需要加 p 参数,如果不加,则会报错;
3) addr1,addr2      指定addr1行到addr2行的内容进行处理;
      例:sed -n '3,15d'  
      删除3--15行的内容
4) addr1,+N     指定 从addr1行开始,往后N行 的内容来进行处理;
      例:sed -n '50,+36p'  
      从第50行开始打印,直到其后的36行全部打印出来;
5) addr1,~N     从addr1行开始往后走,直到行数是 N 的倍数为止;
      例:sed -n '5,~3p'  
      打印5--9行,9是3的3倍,因此从第5行开始,打印到第9行;

上面例子中的 addr1 都可以使用正则表达式,如 sed -e '/ERROR/,+10p' -e '/INFO/,~5p';
但是 first~step 中的 first 不可以使用正则表达式。

=====================================================================================
阅读(729) | 评论(0) | 转发(0) |
0

上一篇:linux 杂记

下一篇:awk归纳整理

给主人留下些什么吧!~~