文本:- [11111]
- kk=11
- aaa=111
- [22222]
- kk=22
- aaa=222
- aaa=xxx
- aaa=xxx
- [33333]
- kk=33
- aaa=333
所谓的段落,就是一个[xxx]标签到下个标签之前的内容,我们要替换段落[22222]里的第二个aaa后面的值,这里特别注意段落[22222]可能出现在文本中的任意位置。段落里的内容也是不固定的。所以不能按照固定的行,或前后特征来判断,唯一的两个条件就是段落标签[22222],和该段落内出现第二个aaa替换后面的内容为TEST:
- [11111]
- kk=11
- aaa=111
- [22222]
- kk=22
- aaa=222
- aaa=TEST
- aaa=xxx
- [33333]
- kk=33
- aaa=333
- sed -n '1h;/\[/!{1!H;$!b};1!{x;/\[22222\]/s/\(aaa=\)[^\n]*/\1TEST/2;p}' file
[解析]
按我们以前讲过的,用 h 和 H 搭配,把文本读入 hold space 中,不是最后一样的话都 b 跳转去行末,等于说跳过后面的命令(主要是避开后面的 p 打印)。当匹配到下个段落的标签时,就把两个空间的内容交换,再看现在 pattern space 里的内容是否匹配 [22222] 标签,如果匹配就把 aaa= 标记,替换第两个,最后再把该段落打印出来。
- sed '/\[22222]/{:a;N;s/\(aaa=\)[^\n]*/\1TEST/2;t;/\n\[/!ba}' file
-
-
sed '/\[22222]/{:a;N;/\n\[/!{$!ba};s/\(aaa=\)[^\n]*/\1TEST/2}' file
[解析]
这是:a;N;ba搭配的用法,因为文本是全部会打印的,所以没必要 -n 参数再 p 打印,前面不匹配到标签的一律放过,直到匹配到 [22222] 标签后,开始 N 读取下一行,然后尝试替换第2个 aaa= 内容,如果不成功则 t 跳转不会生效。如果没有替换成功的话再看是否匹配 /\n\[/ 如果匹配到就不会跳转去 :a 标签处,这么做的用意在于当[22222]段落中的 aaa 数量没有达到两个,不满足替换的时候,为了防止继续往下读,把 [33333] 标签中的第一个 aaa 读取进入 pattern space 里凑足了两个 aaa 结果给替换了, t 跳到命令末尾,结束了循环。这样就存在了漏洞,所以在跳转的地方必须加上判断,一定要注意排除下一个特征的情况,否则不跳出这个循环会读取下一个段落的
第二个命令比第一个略长一点,但是从效率上讲要略微高一点点,以为它不用每次 N 读去下一行的时候就起尝试替换一次,而它是循环一直读到下一个标签或者最后一行才中止,统一进行一次性替换,替换掉第二个aaa后面的值。
- awk -vRS='\\[[^]]+]\n' 'i~/\[22222]/{$0=gensub("(aaa=)[^\n]*","\\1nnn",2)}{printf i $0;i=RT}' file
[解析]
把标签通过正则设定为RS,把上一个RT保存给变量,然后匹配后一个段落,替换思路和sed是一样的。
- awk '/\[22222]/{f=1;print;next}f{if(/aaa/&&++i==2)$0="aaa=TEST"}/\[/{f=0}1' file
[解析]
这是用的变量开关,允许我这样叫它,因为只有匹配到[22222]时才会把这个开关打开。然后通过变量i来记数,当它等于2时才执行这个替换。然后匹配到下一个标签时,则关闭这个变量开关。
阅读(7014) | 评论(2) | 转发(1) |