如何使用awk或sed工具使用多行模式从平面文件中过滤数据?

时间:2015-06-09 15:27:31

标签: regex awk sed

这是我在这个网站上的第一篇文章。我可能不是很容易使用awk或sed语言的问题。在我的文件中是这样的数据:

A
B
C
[Start]D
E
F
[/End]
G
...
[Start]H
I
J
[/End]
...
K

我需要以下结果:

A
B
C
[Open]D E F[/Close]
G
...
[Open]H I J[/Close]
...
K

目前我还没有使用awk代码:

BEGIN {
    step=0
}

/[\/End]/ {
    if(step==3) print "[/Close]"
    step=0
}

step==2 {
    print
    step=3
}

step==1{
    print
    step=2
}

/[Start]/ {
    print "[Begin]"
    step=1
}

step=0{
    print
}

非常感谢你的回答。我希望能在这儿待一会儿。干杯! P上。

3 个答案:

答案 0 :(得分:2)

使用sed,你可以写(GNU sed语法,对于BSD sed见下文):

sed '/\[Start\]/ { s//[Open]/; :a \,\[/End\],! { s/\n/ /; N; ba }; s,,[/Close],; s/\n// }' filename

这应理解如下:

/\[Start\]/ {        # If a line contains [Start]
  s//[Open]/         # replace it with [Open] (an empty regex reattempts the most
                     # recently used regex, which was \[Start\])
  :a                 # jump label for looping
  \,\[/End\],! {     # Until we find [/End]
    s/\n/ /          # replace newlines with spaces (this does nothing the first
                     # time around, but since we don't want to replace the last
                     # newline with a space but an empty string, we have to
                     # isolate it somehow; this works for that
    N                # fetch next line, append it to what we already have
    ba               # go back to a
  }
  s,,[/Close],       # replace the [/End] we just found with [/Close]
  s/\n//             # and replace the last newline with nothing, to get the
                     # spaces right.
}

请注意,要使用BSD sed工作,必须稍微修改一下调用:

 sed -e '/\[Start\]/ { s//[Open]/; :a' -e '\,\[/End\],! { s/\n/ /; N; ba' -e '}; s,,[/Close],; s/\n// }' filename

这是因为BSD sed不像GNU sed那样以分号终止标签名称。除了在标签名称之后拆分代码的-e之外,它是相同的代码。

进一步请注意,只有[Start] .. [/End]标记未嵌套时,此方法才有效。如果它们是,你将要抛弃sedawk并至少使用Perl(它支持regexes 1 中的递归)。

1 好吧,它称之为“正则表达式”;它有点用词不当,因为它们并不局限于普通语言,而Perl对它们的所有东西都是如此。重点是:嵌套标签不再是常规语言,所以你需要/想要它的东西。

答案 1 :(得分:2)

$ cat tst.awk
sub(/^\[Start\]/,"[Open]")  { ors=ORS; ORS=OFS }
sub(/^\[\/End\]/,"[Close]") { ORS=ors }
{ print }

$ awk -f tst.awk file
A
B
C
[Open]D E F [Close]
G
...
[Open]H I J [Close]
...
K

如果你在每个" [关闭]之前关心额外的空间"我们可以做一些不同的事情,但它会有点复杂。例如:

$ cat tst.awk
sub(/^\[Start\]/,"[Open]")  { f=1; rec=$0; next }
sub(/^\[\/End\]/,"[Close]") { f=0; $0=rec $0 }
f { rec = rec OFS $0; next }
{ print }

$ awk -f tst.awk file
A
B
C
[Open]D E F[Close]
G
...
[Open]H I J[Close]
...
K

答案 2 :(得分:1)

这个awk会完成大部分操作,但会在[\ Close]

之前留空
awk '/Start/{ORS=FS} /End/{ORS=RS} sub(/Start/,"Open") sub(/End/,"Close") 1' file

很容易在另一个传递中修剪它(管道先前输出到此脚本)

awk 'sub(/ \[/,"\[") 1'