原文来自互联网,以下仅是自己在阅读过程中,稍作整理用于自己理解。
Sed示例-----初体验
example1:
$ sed -e 'd' /etc/services
这条command做了哪几件事:
1. (set opinion调用sed) 用一个编辑命令 'd' 调用 sed;
2. (基于行模式输入) sed 打开 /etc/services文件,将一行读入其模式缓冲区;
3. (do expected action) 执行编辑命令(“删除行”);
4. (基于行模式输出) 然后打印模式缓冲区(缓冲区已为空)到stdout;
5. (go to 2 in loop until end)然后,它对后面的每一行重复这些步骤。
可以看出以下点:
1. sed对待编辑的文本是做read操作,不会modify文本;
2. sed对文本的操作,是以批处理形式执行的;
3. 养成使用单引号来括起 sed 命令的习惯是个好注意,这样可以禁用 shell 扩展。
example2:
$ sed -e '1d' /etc/services | more
对比发现,管道符左边只比example1在编辑命令d前多了一个数字1。这个数字的逻辑量意义是行地址,用来界定后面编辑命令的有效操作范围。
example1中没有数字,编辑命令d的操作范围是全文本,example2中有数字1,编辑命令d的操作范围只针对第一行,即删除文本的第一行。进一步地,如果
$ sed -e '1,10d' /etc/services | more
表示删除从第一行到第十行的数据(包括第一行和第十行)。
带规则表达式的地址
上面的示例展示了数字地址来索引特定行,接下来展示带正则表达式匹配特定行:
sed -e '/^#/d' /etc/services | more
这条command将删除/etc/services内存副本中的所有以#开头的注释行。
规则表达式总结
使用规则表达式来表示可能会在文本中发现的模式。下面是在规则表达式中常见的特殊字符:
^ 与行首匹配
$ 与行尾匹配
. 与任一字符匹配
* 将与前一个字符(块)的零个或多个匹配
[] 与[ ]内的所有字符匹配
examples:
/./ 将与包含至少一个字符的任何行匹配
/../ 将与包含至少两个字符的任何行匹配
/^#/ 将与以'#'开始的任何行匹配
/^$/ 将与所有空行匹配
/}$/ 将与以 '}' (无空格)结束的任何行匹配
/} *$/ 将与以 '}' 后面跟有零或多个空格结束的任何行匹配
/[abc]/ 将与包含小写字符'a'、'b'或'c'的任何行匹配
/^[abc]/ 将与以'a'、'b'或'c'开始的任何行匹配
sed使用regexp的通用模板:
$ sed -e -n '/regexp/p' /path/to/my/test/file | less
'-n' 选项,该选项告诉 sed 除非明确要求打印模式空间,否则不这样做,而p命令正是用来显式地要求sed开启打印模式。
有关地址的更多内容
和行索引地址区间类似,规则表达式也存在索引地址区间,如下:
$ sed -n -e '/BEGIN/,/END/p' /my/test/file | more
/BEGIN/ 是索引地址区间左端点,它指明了从匹配到的第一个行开始
/END/ 是索引地址区间右端点,它指明了从匹配到的第一个行结束
所有中间行作为操作域。例如:
匹配文件data.txt的内容是:
p1
p1
p2
p2
那么,
$ sed -n -e '/p1/, /p2/p' data.txt
p1 <----------------------- 这是/p1/模式匹配到的第一个匹配行
p1
p2 <----------------------- 这是/p2/模式匹配到的第一个匹配行
代码格式良好时,利用下面的command可以输出source.c中的main函数:
$ sed -n -e '/main[[:space:]]*(/,/^}/p' source.c | more
[[:space:]] 是一个特殊的关键字,它告诉 sed 与 TAB 或空格匹配。或者[ -V]替换之。
上面的命令意思就是:
打印
从
...main (... <-------开始行
到
} <-------结束行
替换!
sed 最有用的命令之一,替换命令,可以将特定字符串或匹配的规则表达式用另一个字符串替换。
$ sed -e 's/foo/bar/' myfile.txt <---------- 将 myfile.txt 中每行第一次出现的 'foo'(如果有的话)用字符串 'bar' 替换,然后将该文件内容输出到标准输出。
$ sed -e 's/foo/bar/g' myfile.txt <---------- 将myfile.txt 中每行出现的 所有'foo'(如果有的话)用字符串 'bar' 替换,然后将该文件内容输出到标准输出。
在最后一个斜杠之后附加的 'g' 选项告诉 sed 执行全局替换。
s/// 替换命令说明:
它是一条命令,g是它的一个选项。
可以使用行索引地址区间,来约束操作域。如: $ sed -e '1,10s/foo/bar/g' myfile.txt 表示对1到10行执行替换命令
's///' 命令的另一个妙处是 '/' 分隔符有许多替换选项。如果正在执行字符串替换,并且规则表达式或替换字符串中有许多斜杠,则可以通过在 's' 之后指定一个不同的字符来更改分隔符。在Perl的正则表达式中,有着类似的语法规则。Unix味儿十足。
规则表达式混乱 ----------- 既是文本处理的法宝,也是一层层一眼望不穿的迷雾
解析原则:
1. 从左向右,按照语法规则划分出意群,对每个意群做代换;
2. 对每一个意群做语义分析;
2. 对意群的划分,按照最小意群规则,依次往外拓展。外层的元操作是对内层的意群的叠加描述。
example:
myfile.html的内容如下
<b>This</b> is what <b>I</b> meant.
我想把所有的tag删掉,即把所有的<...>模式匹配出来的字符串从中删去。首先尝试下面的命令和对应的结果:
$ sed -e 's/<.*>//g' myfile.html
meant.
没有达到预期的编辑效果,why?
原因就在于s/// 命令做最长匹配,那 <.*>在myfile.html中的最长匹配就是<b>This</b> is what <b>I</b>
由此可知,使用s///失败的根本原因在于没有正确地分析、设计出对应的匹配模式。
接下来我们来尝试设计出可用的匹配模式,第一次可能觉得有点难以理解,但多尝试就会发现这是一个很自然的过程:
和解析正则表达式的思维方式正好相反,设计正则表达式按照由外到内:
首先我们用自然语言来清晰地做一次描述:
匹配出文本中所有的<...>标签,...内容可以是任意不包含>的字符串。
==> /<P1>/g,P1不包含>的任意字符串 就是我们要找的匹配模式
==> 接下来实现P1,根据P1的描述,P1=P2*,P2是除>以外的任意字符
==> 接下来实现P2,根据P2的描述,P2=[^>]
==>至此分解求值结束,逆向回代可得原来的匹配模式是/<[^>]*>/g