SED一句话技巧 Mar.23.2001
编:Eric Pement
version 5.1
译:段志岩 @ 吞吴
本文的最新版本通常在:
http://www.student.northpark.edu/pemente/sed/sed1line.txt
http://www.cornerstonemag.com/sed/sed1line.txt
本文的葡萄牙文版本可以在这里找到:
http://www.lrv.ufsc.br/wmaker/sed_ptBR.html
一、文本间距
二倍行距
sed G
在含空行的文件的行与行之间添加空行。输出文件的行与行之间的空行数不会多于一行。
sed '/^$/d; G'
三倍行距
sed 'G; G'
取消二倍行距(假定偶数行为空行)
sed 'n; d'
二、编号方式
标记行号(纯左对齐)。使用tab替代space可以保持页边距。
sed = filename | sed 'N; s/n/t/'
标记行号(号码在左,文本右对齐)。
sed = filename | sed 'N; s/^/ /; s/*(.{6,})n/1 /'
标记行号,但只输出非空的行。
sed '/./=' filename | sed '/./N; s/n/ /'
计算行数(类似于”wc -l”)
SED -N '$='
三、文本转换与替换
UNIX环境下:将DOS换行符转(CR/LF)换成Unix格式
sed 's/.$//' #假定所有行均以CR/LF结尾
sed 's/^M$//' #在bash/tcsh下,按Ctrl-V然后按Ctrl-M
sed 's/x0D$//' #gsed 3.02.80中可用,但第一个脚本更简单一些
UNIX环境下:将UNIX换行符转换成DOS格式
sed "s/$/'echo -e r'/" #ksh命令行模式下
sed 's/$'"/'echo r'/" #bash命令行模式下
sed "s/$/'echo r'/" #zsh命令行模式下
sed 's/$/r/' #gsed 3.02.80
DOS环境下:将Unix换行符(LF)转换成DOS格式
sed "s/$//" #方法一
sed -n p #方法二
DOS环境下:将DOS换行符(CR/LF)转换成Unix格式
#此操作不能在DOS版的sed中实现。转用tr来代替
tr -d r outfile #GNU tr version 1.22或以上
删除行首空白(space, tab),使文本全部左移。
sed 's/^[ t]*//' #请参照文末关于't'的注解
删除行末空白(space, tab)
sed 's/[ t]*$//' #请参照文末关于't'的注解
同时删除行首行尾空白(space, tab)
sed 's/^[ t]*//; s/[ t]*$//'
在每行行首插入5个空格(使页面右移)
sed 's/^/ /'
使文本全部靠右对齐(宽度为79列)
sed -e; a -e 's/^.{1, 78]$/ &/;ta'
使文本居中(宽度为79列)。
#方法一中,行首空格仍然有效,行尾被加上了空白以填补不足。
#方法二中,行首空白在居中过程中被抛弃,行尾也没有补白的空白。
sed -e :a -e 's/^.{1, 77}$/ &/;ta' #方法一
sed -e :a -e 's/^.{1, 77}$/ &/;ta' -e 's/( *)1/1/' #方法二
查找与替换:将每行中的”foo”替换成”bar”
sed 's/foo/bar/' #只替换每行中的第一个实例
sed 's/foo/bar/4′ #只替换每行中的第四个实例
sed 's/foo/bar/g' #替换一行中出现的所有实例
sed 's/(.*)foo(.*foo)/1bar2/' #替换每行倒数第二个实例
sed 's/(.*)foo/1bar/' #只替换替换每行倒数第一个实例
查找与替换:将不含”baz”的行中的”foo”替换成”bar”
sed '/baz/!s/foo/bar/g'
将”scarlet””ruby””puce”换为”red”
sed 's/scarlet/red/g; s/ruby/red/g; s/puce/red/g' #适用于大部分sed
gsed 's/scarlet|ruby|puce/red/g' #只适用于GNU的sed
颠倒行序(使末行变首行,首行变末行) (类似tac)
#HHsed的特性(或者是Bug)会导致空行被删除
sed '1!G;h;$!d' #方法一
sed -n '1!G; h; $p' #方法二
反序输出每一行(类似rev)
sed '/n/!G;s/(.)(.*n)/&21/;//D;s/.//' /*这里似乎有点问题*/
如果某行以反斜线结尾,则将下一行接在它的后面
sed -e :a -e '/$/N; s/n//; ta'
如果某行以等号开头, 则将它接在上一行的行尾,并将等号用一个空格来替换
sed -e :a -e '$!N; s/n=/ /; ta' -e 'P;D'
给数值字符串加逗点,如将”1234567″变为”1,234,567″
gsed ':a; s/B[0-9]{3}>/,&/;ta' #GNU sed
sed -e :a -e 's/(.*[0-9]([0-9]{3})/1,2/;ta' #其它sed
给带小数点和负号的数字加逗点(GNU sed)
gsed ':a;s/(^|[^0-9])([0-9]+)([0-9]{3}/12,3/g;ta'
每隔五行加入一个空行
gsed '0~5G' #只适用于GNU sed
sed 'n;n;n;n;G' #其它sed
四、选择输出特定的行
输出文件的前十行(与head类似)
sed 10q
输出文件和第一行
sed q
输出文件和末尾10行(与tail类似)
sed -e :a -e '$q; N;11, $D;ba'
输出文件的最后两行
sed '$!N; $!D'
输出文件的末行
sed '$!d' #方法一
sed -n '$p' #方法二
输出符合正则表达式的行(类似grep)
sed -n '/regexp/p' #方法一
sed '/regexp/!d/ #方法二
输出不符合正则表达式的行(类似grep -v)
sed -n '/regexp/!p' #方法一,其实现与上面的描述是一致的
sed '/regexp/d' #方法二,这样的语法更简单
输出某一正则表达式之前的一行,但不输出含有该正则表达式的行
sed -n '/regexp/{g;1!p;};h'
输出某一正则表达式之后的一行,但不输出含有该正则表达式的行
sed -n'/regexp/{n;p;}'
输出某一正则表达式之前和之后的一行,以及该正则表达式出现的行的行号(与”grep -A1 -B1″)
sed -n -e '/regexp/{=;x;l!p;g;$!N;p;D;}' -e h
提取含AAA和BBB和CCC(任意顺序)的行
sed '/AAA/!d; /BBB/!d; /CCC/!d'
提取含AAA和BBB和CCC(按该顺序)的行
sed '/AAA.*BBB.*CCC/!d'
提取含AAA或BBB或CCC的行(类似于egrep)
sed -e '/AAA/b' -e '/BBB/b' -e'/CCC/b' -e d #适用于大部分sed
gsed '/AAA|BBB|CCC/!d' #只适用于GNU sed
输出含有AAA的段落(空行分隔段落)
#以下脚本在HHsed v1.5 中要在'x;'后加上'G;'
sed -e '/./{H;$!d;}' -e 'x; /AAA/!d;'
输出含AAA和BBB和CCC(任意顺序)的段落
sed -e '/./{H; $!d;}' -e 'x;/AAA/!d; /BBB/!d; /CCC/!d'
输出含AAA或BBB或CCC的段落
sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
gsed '/./{H;$!d;};x;/AAA|BBB|CCC/b;d' #只适用于GNU sed
输出长度不小于65个字符的行
sed -n '/^.{65}/p'
输出长度小于65个字符的行
sed -n '/^.{65}/!p' #方法一,其实现与上面的描述是一致的
sed '/^.{65}/d' #方法二,这样的语法更简单
输出从给定正则表达式到文件末尾的部分
sed -n '/regexp/,$p'
输出指定行号的部分(如输出8-12行,包括本身)
sed -n '8, 12p'
输出第52行
sed -n '52p' #方法一
sed '52!d' #方法二
sed '52q;d' #方法三(在文件较大时效率更高)
从第三行开始,每七行输出一次
gsed -n '3~7p' #只适用于GNU sed
sed -n '3,${p;n;n;n;n;n;n;} #适用于其它sed
输出两个正则表达式之间的部分(包含本身)
sed -n "/regexp1/,/regexp2/p' #大小写敏感
五、选择性删除特定行
输出除两正则表达式之间部分之外的全部
sed '/regexp1/,/regexp2/d'
删除重复、连续的行(类似uniq)
#一系列重复的行中的第一行将被保留,其余的被删除
sed '$!N; /^(.*)n1$/!P; D'
删除重复、不连续的行
#注意不要造成缓冲区溢出,否则使用GNU sed
sed -n 'G; s/n/&&/; /^([ -~]*n).*n1/d; s/n//; h; P'
删除文件的前十行
sed '1, 10d'
删除文件的末行
sed '$d'
删除文件的最后两行
sed 'N; $!P;$!D;$d'
删除文件的最后10行
sed -e :a -e '$d;N;2,10ba' -e 'P;D' #方法一
sed -n -e :a -e '1, 10!{P;N;D;};N;ba' #方法二
每8行删除一次
gsed '0~8d' #只适用于GNU sed
sed 'n;n;n;n;n;n;n;d;' #适用于其它sed
删除文件中连续的空行(只保留一个空行),包括开头和结尾 (类似cat -s)
sed '/./,/^$/!d' #方法一,文件开头无空行,结尾保留一个空行
sed '/^$/N;/n$/D' #方法二,文件开头允许一个空行,结尾无空行
删除所有连续的空行,除了前两个之外
sed '/^$/N;/n$/N;//D'
删除文首所有的空行
sed '/./,$!d'
删除文末所有空行
sed -e :a -e '/^n*$/{$d;N;ba' -e '}' #适用于所有sed
sed -e :a -e '/^n*$/N;/n$/ba' #同上,但不包括gsed 3.02*
删除每一段落的最后一行
sed -n '/^$/{p;h;};/./{x;/./p;}'
六、特殊用法
去除man文档中的特殊字符(char,backspace)
#如果你使用Unix System V 或者 bash shell,'echo'命令需要一个'-e'参数
sed "s/.`echo b`//g" #Unix 环境下双引号是必需的
sed 's/.^H//g' #在bash/tcsh, 按Ctrl+V 然后按Ctrl+H
sed 's/.x08//g' #sed v1.5的十六进制表达式
获取Usenet/e-mail的头信息
sed '/^$/q' #删除第一个空行后的所有行
获取Subject头信息,但是要删除原始的”Subject:”部分
sed '/^Subject: */!d; s///;q'
获取返回地址头信息
sed '/^Reply-To:/q; /^From:/h; /./d;g;q'
为每一行行首添加一个尖括号和一个空格(引用)
sed 's/^ /> /'
删除行首的尖括号和空格(去除引用)
sed 's/^> //'
去除大部分HTML标签(适用于多行标签)
sed -e :a -e 's/<[^>]*>//g;/
提取多部分的UU编码的二进制文件,删除不必要的头信息,只留下UU编码的部分
#传送给sed的文件必须按适当的顺序
#版本一可以在命令行下输入
#版本二可以写成可执行的Unix脚本
sed '/^end/,/^begin/d' file1 file2 ... fileX | uudecode #版本一
sed '/^end/,/^begin/d' "$@" | uudecode #版本二
七、典型用法
sed接受一个或多个命令并按顺序对每一行输入执行全部这些命令。当所有的命令都对第一行执行以后,这一行被输出,然后开始对第二行进行处理,如此循环。前面的例子假定输入来自标准输入设备(如控制台,一般是通过管道的输入)。如果输入不来自stdin,可以在命令行中添加一个或多个文件名。输出将被送到标准输出设备。
如:
cat filename | sed '10q' #使用管道输入
sed '10q' filename #同样的效果,不过免去了使用cat
sed '10q' filename > newfile #重定向输出到磁盘
更多语法讲解,包括用包含编辑命令的文件来代替命令行输入,请参阅
- 《sed&awk,2nd Edition》by Dale Dougherty & Arnold Robbins(O’Reilly, 1997; http://www.ora.com)
- 《UNIX Text Processing》by Dale Dougherty & Tim O’reilly(Hayden Books, 1987)
或者参考Mike Arst写的教程。
要想发掘出sed的全部力量,你必须懂得”正则表达式”。关于正则表达式,请参阅:
- 《Mastering Regular Expressions》 by Jeffrey Freidl (O’Reilly, 1997)
Unix系统中的man手册也会有所帮助(试试”man sed”,”man regexp”, 或者看看”man ed”中关于正则表达式的部分),不过man手册是出了名的难懂。它并不是用来教那些sed和正则表达式的初学者的,它是一个写给那些已经掌握了这些工具的人的手册。
八、引号的语法
以上的例子使用单引号而不是双引号来容纳编辑命令,因为sed通常用于Unix平台。单引号可以防止Unix shell对’$”`’这样的字符进行解释和展开,如果它们被包含在双引号中,那么这些就会发生。使用csh或其衍生而来的shell的人,即使是在单引号中,还需要把’!’前加上反斜线才能正确的运行上面的例子。DOS版本的sed总是要求用双引号而不是单引号来包含编辑命令。
九、在sed脚本中使用’t’
为了文档的清晰,我们使用了表达式’t’来代表一个脚本中的制表符。但是多数sed版本不识别’t’这个缩写,因此,在编写这些脚本时你要按TAB键。’t’这个缩写是为awk,perl,HHsed,sedmod,GNU sed v3.02.80的正则表达式所支持的元字符。
十、sed的版本
sed的版本之间确不太一样,可能在语法上有些许的不同。具体说来,很多版本不支持编辑命令中的标签(:name)或分支符(b,t),除了尾部的这些元素。我们使用了可以为大多数sed用户移植的语法,尽管GNU版本的sed允许更简洁的语法。
当读者看到一个相当长的命令像:
sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
知道GNU可以让你把它减缩为:
sed '/AAA/b;/BBB/b;/CCC/b;d' #或者更简单
sed '/AAA|BBB|CCC/b;d'
是很令人高兴的。
另外,记住:很多sed的版本接受这样的命令像”/one/ s/RE1/RE2/”, 有一些不允许”/one/! s/RE1/RE2/”这样在’s’前包含空格的命令。这时,在输入命令的时候要去除空格。
十一、优化速度
当执行速度需要优化(由于输入文件很大或者处理器、CPU较慢)时,如果的替换命令前给出查找命令,替换操作会变得更快。如:
sed 's/foo/bar/g' filename #标准的替换操作
sed '/foo/ s/foo/bar/g' filename #这样执行起来更快
sed '/foo/ s//bar/g' filename #sed速记语法
在进行行的选择和删除操作时,如果你操作的行只是包含在文件前面的一部分时,在脚本中加入一个退出命令’q’会大幅缩减处理大文件的时间。如:
sed -n '45,50p' filename #输出45-50行
sed -n '51q; 45,50p' filename #同样的功能,但执行得更快