匹配多线模式非贪婪与sed?

时间:2014-08-11 11:01:29

标签: regex sed multiline

infile中:

[start] cmd1
afadfadf
dafdf
[ok] cmd1
[-] cmd2
[-] cmd3
[start] cmd4
dfdafadf
d
afasdf

daf
[stop] cmd4
[-] cmd5
[-] cmd6
[start] cmd1
adfadd
dafa
dfdd33r55ae
[ok] cmd1
[-] cmd7
[start] cmd8
error...

[stop] cmd8
[-] cmd9
[start] cmd10
exit xx

[stop] cmd10
[-] cmd
[start] cmd1
[ok] cmd1

我想打印所有块,如:[start] ... [stop] cmd ...

结果应为:

[start] cmd4
dfdafadf
d
afasdf

daf
[stop] cmd4
[start] cmd8
error...

[stop] cmd8
[start] cmd10
exit xx

[stop] cmd10

我怎么能用sed做到这一点?

sed -n '/\[start\]/I,/\[stop\]/I p'无效,因为范围运算符在找到下一个[停止]之前不会停止。

编辑: 使用@jaybee sed脚本后,我发现当[停止]行多于起始行 时,它仍然有一些问题 ,例如:

infile2

[start] cmd1
afadfadf
dafdf
[ok] cmd1
[-] cmd2
[-] cmd3
[start] cmd4
dfdafadf
d
afasdf

daf
[stop] cmd4
[-] cmd5
[-] cmd6
[start] cmd1
adfadd
dafa
dfdd33r55ae
[ok] cmd1
[-] cmd7
[start] cmd8
error...

[stop] cmd8
[-] cmd9
[stop] sum
[stop] cmd1
[stop] cmd2
[start] cmd10
exit xx

[stop] cmd10
[-] cmd
[start] cmd1
[ok] cmd1

它仍会输出额外的[stop]行,如下所示:

[start] cmd4
dfdafadf
d
afasdf

daf
[stop] cmd4
[start] cmd8
error...

[stop] cmd8
[stop] cmd8
[-] cmd9
[stop] sum
[stop] sum
[stop] cmd1
[stop] cmd2
[stop] cmd2
[start] cmd10
exit xx

[stop] cmd10

所以我决定修改sedsrc来解决这个问题:

#n
/^\[start\]/I {h;d}
#if match [start] create a new hold buffer then delete the pattern space
/^\[stop\]/I {
#if match [stop] do this
H;x
#append line into hold buffer and then swap the hold buffer to pattern space
/^\[start\]/I{p;d}
#if the buffer contain [start], then it is a complete [start]...[stop] block, print the block,start over with next line
d
#if does not contain [start],start over with next line
}
/^\[.+\]/ {
#if it is other control word, do this
h;d
# clear and put current line to hold buffer, start over with next line
}

H
#append non-control line into hold buffer

现在它运行正常,欢迎将来讨论如何使脚本更简洁。

3 个答案:

答案 0 :(得分:5)

好的,所以我建议您使用保留缓冲区,每当看到新的[start]时都将其刷新,并在看到[stop]时打印它。这给出了以下脚本:

#n
/^\[start\]/I {
    h;n
}
/^\[stop\]/I {
    H;x;p;n
}
H

你把它放在例如sedscr然后运行它以获得以下结果:

$ sed -f sedscr infile
[start] cmd4
dfdafadf
d
afasdf

daf
[stop] cmd4
[start] cmd1
adfadd
dafa
dfdd33r55ae
[stop] cmd1
[start] cmd8
error...

[stop] cmd8
[start] cmd10
exit xx

[stop] cmd10

解释

在行的开头看到[start](带有I标志,因为您似乎想要不区分大小写),将该行放入保留空间,删除其先前的内容({ {1}})然后输入下一行(h)。

当人们看到n时,将该行附加到保留空间([stop]),然后交换模式空间并保留空间(H)以打印模式空间({{1然后输入下一行(x)。

在所有其他行上,只需将该行附加到当前保留空间(p)。

顺便说一句,我脚本开头的n相当于命令行上的H:请求 sed 不将模式空间输出到输出流,除非#n命令询问。

答案 1 :(得分:3)

以下是应该有效的awk

awk '/^\[start\]/ {i=1;delete a}  {a[i++]=$0} /^\[stop\]/ {for (j=1;j<i;j++) print a[j]}' file
[start] cmd4
dfdafadf
d
afasdf

daf
[stop] cmd4
[start] cmd1
adfadd
dafa
dfdd33r55ae
[stop] cmd1
[start] cmd8
error...

[stop] cmd8
[start] cmd10
exit xx

[stop] cmd10

如果start具有end,则仅从start打印 每次看到stop时,它都会重置数组并开始向其存储数据 如果找到{{1}},则打印出阵列。

答案 2 :(得分:0)

这可能适合你(GNU sed):

sed ':a;/^\[start\|stop\]/I{:b;$!{n;/^\[/ba;bb}};d' file

如果该行开始[start][stop](大写或小写),请将其打印以及不会开始[的所有后续行。如果下一行以[循环开头并重新开始检查,则删除它。

编辑:

另一个答案可能是:

sed '/^\[start\]/I{h;d};H;/^\[stop\]/I{x;p;x};d' file

编辑:

根据正在修改的问题:

sed '/^\[start\]/I{:a;x;/^\(.*\[stop\][^\n]*\).*/Is//\1/p;x;h;d};H;$ba;d' file