在两个模式之间提取线,包括第一个上方和第二个下方的线

时间:2015-04-30 12:08:28

标签: bash unix awk sed

拥有以下文本文件,我需要在两个模式之间提取和打印字符串,并且还包括第一个模式上方的行和第二个模式后面的行

asdgs sdagasdg sdagdsag
asdfgsdagg gsfagsaf 
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa
line before first pattern
***** FIRST *****
dddd ffff cccc
wwww rrrrrrrr xxxx
***** SECOND *****
line after second pattern
asdfgsdagg gsfagsaf 
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa

我已经找到了许多解决方案,使用sed和awk来提取两个标签,如下所示

sed -n '/FIRST/,/SECOND/p' FileName

但如何在模式之前和之后包含该行?

期望的输出:

line before first pattern
***** FIRST *****
dddd ffff cccc
wwww rrrrrrrr xxxx
***** SECOND *****
line after second pattern

7 个答案:

答案 0 :(得分:3)

正如您要求sed / awk解决方案(并且每个人都害怕ed ;-),这是您在awk中执行此操作的一种方式:

awk '/FIRST/{print p; f=1} {p=$0} /SECOND/{c=1} f; c--==0{f=0}' file

匹配第一个模式时,打印上一行p并设置打印标记f。当第二个模式匹配时,将c设置为1.如果f为1(真),则将打印当前行。 c--==0仅在第二个模式匹配后的行中为真。

另一种方法是通过循环文件两次:

awk 'NR==FNR{if(/FIRST/)s=NR;else if(/SECOND/)e=NR;next}FNR>=s-1&&FNR<=e+1' file file

第一次通过文件循环遍历文件并记录行号。第二个打印范围内的行。

第二种方法的优点是,只需更改脚本中的数字,就可以轻松打印M行之前和N行之后的行。

要使用shell变量而不是硬编码模式,您可以传递如下变量:

awk -v first="$first" -v second="$second" '...' file

然后使用$0 ~ first代替/FIRST/

答案 1 :(得分:1)

我会说

sed '/FIRST/ { x; G; :a n; /SECOND/! ba; n; q; }; h; d' filename

那是:

/FIRST/ {        # If a line matches FIRST
  x              # swap hold buffer and pattern space,
  G              # append hold buffer to pattern space.
                 # We saved the last line before the match in the hold
                 # buffer, so the pattern space now contains the previous
                 # and the matching line.
  :a             # jump label for looping
  n              # print pattern space, fetch next line.
  /SECOND/! ba   # unless it matches SECOND, go back to :a
  n              # fetch one more line after the match
  q              # quit (printing that last line in the process)
}
h                # If we get here, it's before the block. Hold the current
                 # line for later use.
d                # don't print anything.

请注意,BSD sed(与Mac OS X和* BSD一起提供)对分支命令有点挑剔。如果您正在使用其中一个平台,

sed -e '/FIRST/ { x; G; :a' -e 'n; /SECOND/! ba' -e 'n; q; }; h; d' filename

应该有用。

答案 2 :(得分:1)

无论文件中是否有多个范围,这都会有效:

afterRender

如果在FIRST将$ cat tst.awk /FIRST/ { print prev; gotBeg=1 } gotBeg { print if (gotEnd) gotBeg=gotEnd=0 if (/SECOND/) gotEnd=1 } { prev=$0 } $ awk -f tst.awk file line before first pattern ***** FIRST ***** dddd ffff cccc wwww rrrrrrrr xxxx ***** SECOND ***** line after second pattern 更改为数组之前需要打印多行。如果您需要在SECOND之后打印超过1行,请将prev更改为计数。

答案 3 :(得分:0)

x<-sample(1:10,1e5,rep=TRUE)
y<-sample(1:10,1e5,rep=TRUE)


martin  <- function(x, y) {
    x = match(x, unique(x)); y = match(y, unique(y))
    v = x + (max(x) - 1L) * y
    which.max(tabulate(match(v, v)))
}
akrun <-function(x,y) {
    library(data.table)
    xy<-data.frame(x,y)
setDT(xy)[, .N,list(x,y)][which.max(N)]
}
mucio <-function(x,y){
    xy<-data.frame(x,y)
    t<-table(xy)
indexes <- which(t == max(t), arr.ind = TRUE)[1,]
x_value <- dimnames(t)$x[indexes["x"]]
y_value <- dimnames(t)$y[indexes["y"]]
rep_number <- max(t)

}

sam<-function(x,y){
    library(dplyr)
    xy<-data.frame(x,y)
xy %>%
  group_by(x, y) %>%
  tally() %>%
  ungroup %>%
  top_n(1)

}
dimitris<-function(x,y){
    library(dplyr)
xy<-data.frame(x,y)
xy %>% group_by(x, y) %>% summarise(n=n()) %>% 
   ungroup %>% filter(n==max(n)) %>% select(-n)

}

microbenchmark(martin(x,y),akrun(x,y),mucio(x,y),sam(x,y),dimitris(x,y),times=5)

Unit: milliseconds
           expr       min        lq       mean    median         uq
   martin(x, y) 11.727217 14.246913  41.359218 14.384385  82.639796
    akrun(x, y)  4.426462  4.613420   4.866548  4.892432   5.011406
    mucio(x, y) 73.938586 74.037568 103.941459 79.516207 145.232870
      sam(x, y)  8.356426  8.586212   8.919787  8.586521   8.775792
 dimitris(x, y)  8.618394  8.738228   9.252105  9.063965   9.075298
        max neval cld
  83.797780     5  a 
   5.389018     5  a 
 146.982062     5   b
  10.293983     5  a 
  10.764640     5  a
  • POSIX sed版本(GNU sed use sed '#n H;$!d x;s/\n/²/g /FIRST.*SECOND/!b s/.*²\([^²]*²[^²]*FIRST\)/\1/ :a s/\(FIRST.*SECOND[^²]*²[^²]*\)².\{1,\}/\1/ ta s/²/\ /g p' YourFile
  • 如果在同一条线上也采取以下SECOND模式,易于适应至少采取一条新线之间
    • --posix:除非有expres请求(例如#n
    • ,否则不要打印
    • p:将每一行追加到缓冲区,如果不是最后一行,则删除当前行和循环
    • H;$!d:加载缓冲区并用其他字符替换任何新行(此处我使用x;s/\n/²/g)因为posix sed不允许²
    • [^\n]:如果没有模式存在,退出没有输出
    • /FIRST.*SECOND/!b:删除第一个模式之前的所有内容
    • s/.*²\([^²]*²[^²]*FIRST\)/\1/:goto的标签(稍后使用)
    • :a:删除第二个模式后的一行后的所有内容。它采用最大的字符串,因此模式的最后一次出现是参考
    • s/\(FIRST.*SECOND[^²]*²[^²]*\)².\{1,\}/\1/:如果发生上一​​次ta,则必须标记为s///。它直到第一个SECOND模式出现在文件中(在FIRST之后)
    • a:放回新行
    • s/²/\ /g:打印结果

答案 4 :(得分:0)

基于Tom的评论:如果文件不大,我们可以将它存储在数组中,然后循环遍历它:

awk '{a[++i]=$0} /FIRST/{s=NR} /SECOND/{e=NR} END {for(i=s-1;i<e+1;i++) print a[i]}'

答案 5 :(得分:0)

我会亲自与Perl一起做。我们有&#39;范围运营商&#39;我们可以用它来检测我们是否在两种模式之间:

if ( m/FIRST/ .. /SECOND/ ) 

这很容易。什么不那么容易就是“抓住”#39;前一行和下一行。所以我设置了一个$prev_line值,这样当我第一次点击那个测试时,我知道要打印什么。我清楚$prev_line,因为当我再次打印它时它是空的,但也因为那时我可以在范围的末尾发现过渡。

这样的事情:

#!/usr/bin/perl

use strict;
use warnings;

my $prev_line = " ";
while (<DATA>) {
    if ( m/FIRST/ .. /SECOND/ ) {
        print $prev_line;
        $prev_line = '';
        print;
    }
    else {
        if ( not $prev_line ) {
            print;
        }
        $prev_line = $_;
    }
}

__DATA__ 
asdgs sdagasdg sdagdsag
asdfgsdagg gsfagsaf 
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa
line before first pattern
***** FIRST *****
dddd ffff cccc
wwww rrrrrrrr xxxx
***** SECOND *****
line after second pattern
asdfgsdagg gsfagsaf 
asdfsdaf dsafsdfdsfas
asdfdasfadf
nnnn nnnnn aaaaa

答案 6 :(得分:0)

这可能适合你(GNU sed):

sed '/FIRST/!{h;d};H;g;:a;n;/SECOND/{n;q};$!ba' file

如果当前行不是FIRST,请将其保存在保留空间中并删除当前行。如果该行FIRST将其附加到已保存的行,然后在打印其他行并退出脚本时打印这两行以及任何其他行,直至SECOND