一、简介
sed是一种基于流的文本编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。操作有点像vi(强烈建议先学一下vi),功能十分强大!
二、命令格式(详见 man sed)
sed [OPTION]... {script-only-if-no-other-script} [input-file]...
三、基本概念:
1.定址
定址用于决定对哪些行进行编辑。地址的形式可以是数字(行号)、正则表达式、或二者的结合。如果没有指定地址,sed将处理输入文件的所有行。
sed -n '3p' test.c #只打印第三行
2.地址是逗号分隔的,需要处理的地址是这两行之间的范围(包括这两行)。范围可以用数字、正则表达式、或二者的组合表示.
sed -n '100,200p' test.c #只查看文件的第100行到第200行
sed '/FILE/,/log/d' test.c #删除包含"FILE"的行到包含"log"的行之间的行
sed '/FILE/,10d' test.c #删除包含"FILE"的行到第10行之间的行
3.命令
sed命令告诉sed如何处理由地址指定的各输入行(如删除d,替换s),如果没有指定地址则处理所有的输入行。
将字符替换为另一字符(不能对正则表达式使用y命令)
命令 | 功能 |
a\ |
在当前行后添加一行或多行。多行时除最后一行外,每行末尾需用“\”续行 |
c\ | 用此符号后的新文本替换当前行中的文本。多行时除最后一行外,每行末尾需用"\"续行 |
i\ | 在当前行之前插入文本。多行时除最后一行外,每行末尾需用"\"续行 |
d | 删除行 |
h/H | 把模式空间里的内容复制/追加到暂存缓冲区 |
g/G | 把暂存缓冲区里的内容复制到模式空间,覆盖原有的内容/追加到原有内容后面 |
l | 列出模式空间里的内容,不可打印字符表示为 ASCII 码 |
p | 打印行 |
P | Print up to the first embedded newline of the current pattern space。打印由命令N创建的多行模式空间的第一部分(直到嵌入的换行符)。如果没有将N应用于某一行则和p相同 |
n | 读入下一输入行,并从下一条命令而不是第一条命令开始对其的处理 |
N | 将下一个输入行追加到模式空间的内容之后;新添加的行与模式空间的当前内容用换行符分隔(这个命令用于实现跨两行的模式匹配) append the next line of input into the pattern space. |
q | 结束或退出sed |
r | 从文件中读取输入行 |
! | 对所选行以外的所有行应用命令 |
s | s/pattern/replacememt/flag,如果flag是数,则指定匹配位置(默认第一个) 用一个字符串替换另一个 |
g | 在行内进行全局替换 |
w | 将所选的行写入文件。如果文件不存在,这个命令将创建一个文件。如果文件存在,则每次执行脚本时将改写其内容,多重写入命令直接将输出写入到同一个文件并追加到这个文件的末端。 |
x | 交换暂存缓冲区与模式空间的内容 |
y | [address1[,address2]]y/abc/xyz/ 按位置将字符串abc中的字符替换成字符串xyz中相应字符 |
t |
t:lable 测试在寻址的行范围内是否成功执行了替换,如果是,则转移到有label标志的行(参见b和:)。如果没有给出label,控制将转移到脚本的尾部。 If a s/// has done a successful substitution since the last |
: |
:lable 在脚本中标记一行,用于实现由b或t的控制转移。Label最多可以包含7个字符 |
= |
将所寻址的行号写到标准输出 |
b |
b:lable 无条件地将控制转移到脚本的其他位置的:label处。也就是说,label后面的命令是应用于当前行的下一个命令。如果没有指定label, 控制将一直到达脚本的末端,因此不再有命令作用于当前行 |
D |
Delete up to the first embedded newline in the pattern space. Start next cycle, but skip reading from the input if there is still data in the pattern space |
4.选项
选项 | 功能 |
-e | 进行多项编辑,即对输入行应用多条sed命令时使用 |
-n | 取消默认的输出 |
-f | 指定sed脚本的文件名 |
5.退出状态
sed不像grep一样,不管是否找到指定的模式,它的退出状态都是0。只有当命令存在语法错误时,sed的退出状态才不是0。
6.正则表达式元字符,非常有用!(ed,sed,awk,grep,vi 很多都适用哦)
元字符 | 功能 | 实例 |
^ | 行首定位符 | /^my/ 匹配所有以my开头的行 |
$ | 行尾定位符 | /my$/ 匹配所有以my结尾的行 |
. | 匹配除换行符以外的单个字符 | /m..y/ 匹配包含字母m,后跟两个任意字符,再跟字母y的行 |
* | 匹配零个或多个前导字符(不是shell扩展中的任意字符!!) | /my*/ 匹配包含字母m,后跟零个或多个y字母的行 |
[] | 匹配指定字符组内的任一字符 | /[Mm]y/ 匹配包含My或my的行 |
[^] | 匹配不在指定字符组内的任一字符 | /[^Mm]y/ 匹配包含y,但y之前的那个字符不是M或m的行 |
\(..\) | 保存已匹配的字符 | 1,20s/\(you\)self/\1r/ 标记元字符之间的模式,并将其保存为标签1,之后可以使用\1来引用它。最多可以定义9个标签,从左边开始编号,最左边的是第一个。此例中,对第1到第20行进行处理,you被保存为标签1,如果发现youself,则替换为your。 |
& | 保存查找串以便在替换串中引用 | s/my/**&**/ 符号&代表查找串。my将被替换为**my** |
\< | 词首定位符 | /\<my/ 匹配包含以my开头的单词的行 |
\> | 词尾定位符 | /my\>/ 匹配包含以my结尾的单词的行 |
x\{m\} | 连续m个x | /9\{5\}/ 匹配包含连续5个9的行 |
x\{m,\} |
至少m个x | /9\{5,\}/ 匹配包含至少连续5个9的行 |
x\{m,n\} | 至少m个,但不超过n个x | /9\{5,7\}/ 匹配包含连续5到7个9的行 |
注:这些特殊字符仅在搜索串中有特殊意义,替换串中没有!如:
1,$s/[A-Z]/^*/g #把所有大写字母替换为 ^*
四、使用举例:
使用文件为 test.c:
#include <stdlib.h>
#include <stdio.h>
extern char **environ;
int main ( int argc, char *argv[] )
{
char **env = environ;
while (*env)
{
printf("%s\n", *env);
env++;
}
return 0;
}
1.删除(d)
sed '2,$d' test.c #删除第二行到末尾行(特定行)
sed '/test/d' test.c #删除包含test的行
2.单词替换(s)
sed 's/env/e/g' test.c #g为全局替换,若没有,仅替换每行第一个匹配的单词
sed -n 's/env/e/gp' test.c #(-n)选项和p标志一起使用表示只打印那些发生替换的行
sed 's/\<env\>/&ironment/g' test.c #&表示被替换字符串,即将env单词变为本身加ironment,既environment
sed -n 's/\(e\)\(n\)v/fi\2\1/p' test.c #e被标记为1,n被标记为2,替换 env为fine,只打印那些发生替换的行
sed 's@env@e@g' test.c #@为分割符,可自己指定,效果同第一个,也可用#,$等只要不与文本冲突就行
sed -n '1,20s/my$/you/gp' test.c #取消默认输出,处理1到20行里匹配以my结尾的行,把行内所有的my替换为you,并打印到屏幕上。
3.选定行的范围(,)
sed -n '/int/,/return/p' test.c #从包含int的行到return的行都将被打印
sed -n '8,/printf/p' test.c #从第8行到包含printf的行都将被打印
sed '/char/,/return/s/$/addByMe/' test.c #从char到return行的末尾加上字符串 addByMe
4.多点编辑(-e)
sed -e '1,5d' -e 's/env/e/' test.c
sed -e '1,5d' -e 's/env/e/' test.c #执行多条编辑命令,命令的执行顺序对结果有影响
sed --expression='1,5d' --expression='s/env/e/' test.c #效果同上,它能给sed表达式赋值
5.读入文件(r),将一个文本文件中的内容加到当前文件的特定位置上。
sed '/char/r data.txt' test.c #data.txt里的内容被读进来,显示在所有与char匹配的行后面
6.写入文件(w)
sed -n '/char/w data.txt' test.c #所有与char匹配的行都将写入到data.txt里(-n不显示)
7.追加内容(a)
sed '/^int/a\this is append text' test.c #int开头的行被追加了一行,注意a后有反斜杠
8.插入(i)
sed '/printf/i\this is insert line' test.c #在匹配行的前面插入内容
9.下一个(n),使用该命令将获取输入文件的下一行,并将其读入到模式缓冲区中,任何sed命令都将应用到匹配行紧接着的下一行上。
sed '/printf/{n; s/env/e/;}' test.c #将printf的下一行的env替换为e
注:如果需要使用多条命令,或者需要在某个地址范围内嵌套地址,就必须用花括号将命令括起来,每行只写一条命令,或这用分号分割同一行中的多条命令。
10.变形(y),该命令与UNIX/Linux中的tr命令类似,字符按照一对一的方式从左到右进行转换。
sed '1,20y/abc/ABD/' test.c #将1-20行的a,b,c字母分别变为A,B,D
注:正则表达式元字符对y命令不起作用。与s命令的分隔符一样,斜线可以被替换成其它的字符。
11.退出(q),不再进行其它的处理。
sed '10q' test.c #打印前十行后推出(默认打印第一行)
12.保持和获取:(h)和(G)
sed -e '/printf/h' -e '$G' test.c
在sed处理文件的时候,每一行都被保存在一个叫模式空间的临时缓冲区中,除非行被删除或者输出被取消,否则所有被处理的行都将打印在屏幕上。接着模式空间被清空,并存入新的一行等待处理。在这个例子里,匹配printf的行被找到后,将存入模式空间,h命令将其复制并存入一个称为保持缓存区的特殊缓冲区内。第二条语句的意思是,当到达最后一行后,G命令取出保持缓冲区的行,然后把它放回模式空间中,且追加到现在已经存在于模式空间中的行的末尾。在这个例子中就是追加到最后一行。简单来说,任何包含printf的行都被复制并追加到该文件的末尾。
13.保持和互换:(h)和(x)
14.打印(p),参见以上例子
命令p用于显示模式空间的内容。默认情况下,sed把输入行打印在屏幕上,选项-n用于取消默认的打印操作。当选项-n和命令p同时出现时,sed可打印选定的内容。
15.脚本
Sed脚本是一个sed的命令清单,启动Sed时以-f选项引导脚本文件名。Sed对于脚本中输入的命令非常挑剔,在命令的末尾不能有任何空白或文本,如果在一行中有多个命令,要用分号分隔。以#开头的行为注释行,且不能跨行。
五、实战
cat txt1
中国 (中国)
世界
美国
(美国)
加拿大
英国
俄罗斯
(俄罗斯)
阿富汗
希望处理后的结果为:(即将带括号的行和上一行合并)
cat txt2
中国 (中国)
世界
美国(美国)
加拿大
英国
俄罗斯(俄罗斯)
阿富汗
可用sed实现:
sed 'N;s/\n(/ (/;t;P;D' txt1
sed '=;N;s/\n(/ (/;t;P;D' txt1 #打印出行号可有助于理解其工作
1
中国 (中国)
2
世界
3
美国 (美国)
5
加拿大
6
英国
7
俄罗斯 (俄罗斯)
9
阿富汗
也可用perl实现
perl -e 'local $/; $f=<>; $f =~ s/.\(/\(/sg; print $f' txt1
还可用vim实现:
vim -c '%s/\n(/ (/g' -c 'wq' txt1