Bash脚本用于选择以跨越两行并以空行结束的模式开头的范围。桑达?

时间:2012-01-12 22:46:27

标签: bash sed solaris

我有一个文件,其中包含以下格式的条目:

2012-01-12 22:20:21,638 INFO  [Tracer]
  something.of.interest
  ...some number of additional lines...
<<a blank line>>
...other stuff...

我只想挑出第一行包含[Tracer]的文本块,第二行包含 something.of.interest ,以空白行结尾,后面有未知行数第二行。无法更改文件格式。

我可以通过做类似的事情来使用sed来挑选整个块:

gsed -n '/^[0-9]\{4\}[^\[]*\[Tracer\]/,/^$/ p' /path/to/file/to/parse

这会按预期选择整个块,但它匹配第二行不包含something.of.interest的条目。

我可以使用N来获取下一行,然后在当前行和下一行之间进行匹配,只有当我看到[Tracer]和something.of.interest由\分隔时,我才能选择前两行。 ñ但是我似乎无法弄清楚如何抓住以下几行直到我换到换行符。在伪sed我想做这样的事情:

/look for Tracer line 1/{
N
/look for \n.*something.of.interest/
},
/look for blank line for end of range/
p

可悲的是,这实际上并不起作用,通常我会得到并且“未知命令”&#39;信息。

有没有办法在sed中使用模式定义范围,其中开头和结尾可能是多行模式?

解决方案最终需要在Solaris 5.10上运行。 GNU sed(gsed)是可用的,如果在这种情况下这是一个更好的选择,那就是awk。

建议非常感谢。

更新

似乎我可以用awk来做,虽然结果有点可怕。我非常喜欢更优雅的解决方案,这里仍然可以参考awk脚本找到感兴趣的行:

1)创建一个文件something.awk,其中第一行是与第1行匹配的正则表达式:

/\[Tracer\]/ {
  l1=$0
  if (getline <= 0) {
      print "getline failed"
      exit 1
  }
  if (index($0, L2MARKER) > 0) {
      print l1
      print $0
      stop=0
      while(stop != 1) {
         if (getline <= 0) {
            print "getline failed :( ERRNO:" + ERRNO
            exit 1
         }
         print;
         if (length($0) == 0) {
             stop = 1
         }
      }
   }
}

2)从shell调用,类似于 awk -f something.awk L2MARKER =&#39; something.of.interest&#39; the.file.to.parse

7 个答案:

答案 0 :(得分:2)

编辑:有人注意到我的第一个解决方案有点偏差。这是一个尝试修复,虽然我还没有测试它,因为没有测试它的地方。

我在sed

中想出了一些可能对您有用的内容
/Tracer/ { N; /interesting/ { h; :a; n; H; /^$/! ba; g; p } }

接下来是解释!

给出这样的测试文件:

boring
boring
awesome [Tracer]
interesting
totally interesting
and awesome
still interesting
very interesting

back to boring
awesome [Tracer]
Nah just kidding
nope

darn

以上命令扩展为以下内容,并附有说明:

/Tracer/ {          # Looks for Tracer
    N               # Moves on to the next line
    /interesting/ { # Looks to see if "interesting" is in the next line
        h           # Put first two lines in hold space
        :a          # Label "a"
        n           # Move on to next line
        H           # Appends line to hold space
        /^$/! ba    # If not a blank line, branch back to "a"
        g           # Put the hold space into the pattern space
        p           # Print the pattern space
    }
}

而且,应该这样做:

sed -n '/Tracer/ { h; n; /interesting/ { :a; H; n; /^$/! ba }; g; p }' file.txt
awesome [Tracer]
interesting
totally interesting
and awesome
still interesting
very interesting
[blank line]

[blank line]是一个文字空行,但我不能在这里的代码块中表达。显然,您可以轻松地修改/Tracer//interesting/的正则表达式,具体取决于行的具体内容。

答案 1 :(得分:0)

另一个awk解决方案

如果要提取的块总是在文件的顶部,并且输出中只有1或0块:

awk '/\[Tracer\]/{print;f=1;next;} 
{if(NR==2){if(f && $0~/interest/){print;next;} else  exit;} 
if(f && $0) print; else exit;}' file 

如果你想要捕获更多的块:

awk '/\[Tracer\]/{h=$0;f=1;l=NR;next;} 
{if(NR==l+1){if(f && $0~/interest/){print h;print;} else {f=0;h=""; }next;} 
if(f) if($0)print;else f=0; }' file

测试上述

    #input file, in this case, 1st and 3rd blocks should be 
in your output (for solution 2)
kent$  cat file
2012-01-12 22:20:21,638 INFO  [Tracer]
  interesting
  ...some number of additional lines...
  xxx
  yyy
  zzz

...other stuff...
2012-01-12 22:20:21,638 INFO  [Tracer]
  NOT-wanted-NOT
  ...some number of additional lines...
  xxx####
  yyy####
  zzz####

...other ####  stuff...
2012-01-12 22:20:21,638 INFO  [Tracer]
  interest
  ...some number of additional lines...
  xxxWANTWANTWANT
  yyy.WANTWANTWANT
  zzzoWANTWANTWANT

  blahblah

#solution one:
kent$   awk '/\[Tracer\]/{print;f=1;next;} 
    {if(NR==2){if(f && $0~/interest/){print;next;} else  exit;} 
    if(f && $0) print; else exit;}' file 
2012-01-12 22:20:21,638 INFO  [Tracer]
  interesting
  ...some number of additional lines...
  xxx
  yyy
  zzz

#solution two:
kent$  awk '/\[Tracer\]/{h=$0;f=1;l=NR;next;} 
{if(NR==l+1){if(f && $0~/interest/){print h;print;} else {f=0;h=""; }next;} 
if(f) if($0)print;else f=0; }' file 
2012-01-12 22:20:21,638 INFO  [Tracer]
  interesting
  ...some number of additional lines...
  xxx
  yyy
  zzz
2012-01-12 22:20:21,638 INFO  [Tracer]
  interest
  ...some number of additional lines...
  xxxWANTWANTWANT
  yyy.WANTWANTWANT
  zzzoWANTWANTWANT
希望它有所帮助。

答案 2 :(得分:0)

这是另一个awk解决方案:

awk '/\[Tracer\]/{
a=$0;getline;
if ($0~/something\.of\.interest/) {print a; print$0;getline} else next; 
while ($0!~/^$/) {print $0;getline}}' INPUT_FILE

答案 3 :(得分:0)

如果可以保证[Tracer]行正上方有一个空行,

awk 'BEGIN { RS = ""; FS = "\n" } $1 ~ "[Tracer]" && $2 ~ "something.of.interest"' input.file

如果没有,请添加sed以确保它:

sed 's/\(.*\[Tracer\].*\)/\n\1/' test.in |awk \
    'BEGIN { RS=""; FS="\n" } $1 ~ "[Tracer]" && $2 ~ "something.of.interest"'

答案 4 :(得分:0)

Sed有一个专为此类应用设计的保留空间:

sed -n '/^[0-9]\{4\}[^\[]*\[Tracer\]/,/^$/H;/^$/{x;/interesting/p;s/.*//g;x;}'

答案 5 :(得分:0)

这可能对您有用:

 sed -n '/Tracer/,/^$/{H;//{x;/something.of.interest/p}}' file

说明:

  • -n禁止自动打印输出,即打印输出使用pP命令。
  • 将以下命令限制在/Tracer//^$之间的一系列行。
  • 如果这些行符合上述条件,请附加换行符,然后将图案空间(PS)附加到保留空间(HS)。H
  • 如果正则表达式/Tracer//^$/中的任何一个匹配,请执行以下//
    • 将PS替换为HS x
    • 匹配/something.of.interest/,如果是,则打印出HS /.../p
    • 中的所有内容

PS始终首先在感兴趣的行之间附加到HS(H)。 //的第一个匹配将匹配第一个地址,即/Tracer/此时HS变为PS,PS变为HS。没有匹配,因为/something.of.interest/尚未被读入。因为没有匹配,所以没有打印出来但现在HS包含第一个地址,后续行被附加到它,直到第二个地址匹配。交换了PS和HS,这次/something.of.interest/匹配,并打印出/Tracer//^$/之间的所有行。

答案 6 :(得分:0)

我找到Perl以“段落”模式阅读文字:

perl -00 -ne '
  @lines = split /\n/;
  print if $lines[0] =~ /Tracer/ and $lines[1] =~ /something.of.interest/
'