使用awk或sed在两个模式之间进行第n次出现

时间:2012-11-08 02:27:28

标签: shell sed awk

我有一个问题,我想解析一个文件的输出,我想在两个模式之间抓取第n个文本,最好使用awk或sed

category
1
s
t
done
category
2
n
d
done
category
3
r
d
done
category
4
t
h
done

我们只想说这个例子我想在类别和完成之间抓取第三次出现的文本,基本上输出将是

category
3
r
d
done

5 个答案:

答案 0 :(得分:8)

这可能适合你(GNU sed):

'sed -n '/category/{:a;N;/done/!ba;x;s/^/x/;/^x\{3\}$/{x;p;q};x}' file

使用-n选项关闭自动打印。收集categorydone之间的行。将计数器存储在保留空间中,当它达到3时,在模式空间中打印该集合并退出。

或者如果您更喜欢awk:

awk  '/^category/,/^done/{if(++m==1)n++;if(n==3)print;if(/^done/)m=0}'  file

答案 1 :(得分:5)

尝试这样做:

 awk -v n=3 '/^category/{l++} (l==n){print}' file.txt

或者更神秘:

awk -v n=3 '/^category/{l++} l==n' file.txt

如果您的文件很大:

awk -v n=3 '/^category/{l++} l>n{exit} l==n' file.txt

答案 2 :(得分:1)

如果您的文件不包含任何空字符,则此处正在使用GNU sed。这将找到模式范围的第三次出现。但是,您可以轻松地修改它以获得您想要的任何事件。

sed -n '/^category/ { x; s/^/\x0/; /^\x0\{3\}$/ { x; :a; p; /done/q; n; ba }; x }' file.txt

结果:

category
3
r
d
done

说明:

使用-n开关关闭默认打印。在一行的开头匹配“类别”一词。使用保留空间交换模式空间,并在模式的开头附加空字符。在该示例中,如果模式包含两个前导空字符,则将模式拉出holdspace。现在创建一个循环并打印模式空间的内容,直到最后一个模式匹配。找到最后一个模式后,sed将退出。如果未找到,sed将继续读取下一行输入并继续其循环。

答案 3 :(得分:1)

awk -v tgt=3 '
/^category$/ { fnd=1; rec="" }

fnd {
   rec = rec $0 ORS
   if (/^done$/) {
      if (++cnt == tgt) {
         printf "%s",rec
         exit
      }
      fnd = 0
   }
}
' file

答案 4 :(得分:0)

使用GNU awk,您可以将记录分隔符设置为正则表达式:

<file awk 'NR==n+1 { print rt, $0 } { rt = RT }' RS='\\<category' ORS='' n=3

输出:

category 
3
r
d
done

RT是匹配的记录分隔符。请注意,相对于n的记录将会被关闭,因为第一个记录指的是第一个RS之前的记录。

修改

根据Ed的评论,当记录中包含其他数据时,这将不起作用,例如:

category
1
s
t
done
category
2
n
d
done

foo

category
3
r
d
done

bar
category
4
t
h
done

解决这个问题的一种方法是使用第二个(或第一个)awk来清理输入:

<file awk '/^category$/,/^done$/' |
  awk 'NR==n+1 { print rt, $0 } { rt = RT }' RS='\\<category' ORS='' n=3

输出:

category 
3
r
d
done

编辑2

正如 Ed 在评论中指出的那样,上述方法不会搜索结束模式。执行此操作的一种方法是getline(请注意,有一些caveats使用awk getline):

<file awk '
  /^category$/ {
    v = $0
    while(!/^done$/) { 
      if(!getline) 
        exit
      v = v ORS $0
    }
    if(++nr == n) 
      print v
}' n=3

在一行:

<file awk '/^category$/ { v = $0; while(!/^done$/) { if(!getline) exit; v = v ORS $0 } if(++nr == n)  print v }' n=3