1

我得到了一个看起来像这样的文本流:

whatever => foo,
arg => 'some text
   over multiple lines
   sometimes',
bytes => 123,
...

我感兴趣的是 和 之间的arg =>文本bytes =>。所以我过滤掉了这个块

cat mystream | awk '/arg =>/,/bytes =>/'

效果很好。但是现在如果文本中某处有一个单词,我想跳过整个块。类似的东西,grep -v但对于整个街区,而不仅仅是这条线。有任何想法吗?谢谢。

请注意,这不仅限于 awk,这正是我想到的。任何其他工具也可以。

4

3 回答 3

6

使用 /pat1/,/pat2/ 范围通常看起来是个好主意,但是一旦您需要添加条件或做其他事情,它就会变得平淡。恕我直言,你最好只使用这样的标志:

awk '/arg =>/{f=1} f; /bytes =>/{f=0}' file

因为可以在不完全重写的情况下进行扩展。在这种情况下,只需在您处于范围内时(即设置“f”时)建立一个记录,并在合适的情况下在范围末尾打印它。这将始终打印它:

awk '/arg =>/{rec=""; f=1} f{rec = rec $0 ORS} /bytes =>/{ if (f) printf "%s",rec; f=0}' file

并且只有当文本“whatever”出现在记录中时才会打印它:

awk '/arg =>/{rec=""; f=1} f{rec = rec $0 ORS} /bytes =>/{ if (f && (rec ~ "whatever")) printf "%s",rec; f=0}' file

并且只有当文本“whatever”没有出现在记录中时才会打印它:

awk '/arg =>/{rec=""; f=1} f{rec = rec $0 ORS} /bytes =>/{ if (f && (rec !~ "whatever")) printf "%s",rec; f=0}' file

这是您在下面评论中的脚本(稍微重新格式化)

<tcpdump> |
awk '
   /arg =>/ {rec=""; f=1}
   f {rec = rec $0 ORS}
   /bytes =>/ {
      if (rec !~ /menuStructure|session/)
         printf "%s",rec
      f=0
   }
' | sed "s/.*bytes =>.*/\n----------\n/g" | sed "s/arg => //g"

基于此,我认为这个脚本会做你想做的事情:

<tcpdump> |
awk '
   /bytes =>/ {
      if (f && (rec !~ /menuStructure|session/))
         print rec "----------"
      f=0
   }
   f {rec = rec $0 ORS}
   sub(/arg =>/,"") {rec=$0; f=1}
'
于 2012-11-27T13:57:58.687 回答
1
awk '/arg =>/,/bytes =>/ {s=s?s:NR;if($0~/some/)exit; a[NR]=$0;e=NR;}END{for(i=s;i<=e;i++)print a[i]}' file

如果您的块包含“某些”,则上述 oneliner 将不打印任何内容(根据您的示例)。

于 2012-11-27T13:27:55.143 回答
1

这是使用 GNU awk 的一种方法:

m1='arg =>'
m2='bytes =>'
pattern='some'
awk -v RS="$m1|$m2" -v start="$m1" -v end="$m2" -v pattern="$pattern" \
  'RT == end && $0 !~ pattern { print start $0 end }' < mystream

也就是说,在开始和结束标记处拆分流,然后,当找到结束标记并且块不包含$pattern时,打印它。

请注意m1m2pattern都是正则表达式,因此可以根据您的需要进行调整。m1另请注意,如果您的输入块包含或,这将不起作用m2。另请参阅下面的 Ed 说明。

于 2012-11-27T13:57:56.700 回答