awk:仅当特定行不存在时才过滤块

时间:2012-11-27 12:27:00

标签: awk filtering

我有一个看起来像这样的文本流:

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

我感兴趣的是arg =>bytes =>之间的文字。所以我用

过滤掉了这个块
cat mystream | awk '/arg =>/,/bytes =>/'

工作正常。但是现在我想跳过整个块,如果文本中有一个单词的话。像grep -v这样的东西,但对于整个区块而言,不仅仅是线条。有任何想法吗?感谢。

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

3 个答案:

答案 0 :(得分:5)

使用/ 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

只有当记录中出现“what”字样时才会打印出来:

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

只有当记录中没有出现“what”字样时才会打印出来:

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}
'

答案 1 :(得分: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

如果您的块包含“some”,则上面的oneliner将不会打印任何内容(基于您的示例)。

答案 2 :(得分: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都是正则表达式,因此可以根据您的需要进行调整。另请注意,如果输入块包含m1m2,则无法使用此功能。另见Ed的注释。