搜索(例如awk,grep,sed)字符串,然后查找上面的X行和下面的另一个字符串

时间:2013-05-22 14:29:21

标签: bash sed awk grep nawk

我需要能够搜索字符串(让我们使用4320101),在字符串上方打印20行并在此之后打印,直到找到字符串

例如:

Random text I do not want or blank line
16 Apr 2013 00:14:15
id="4320101"
</eventUpdate>
Random text I do not want or blank line

我只想将以下结果输出到文件中:

16 Apr 2013 00:14:15
id="4320101"
</eventUpdate>

我想要的文件中有多个这类文本示例。

我尝试使用以下内容:

cat filename | grep "</eventUpdate>" -A 20 4320101 -B 100 > greptest.txt

但它只会在字符串的两侧显示20行。

注意:
- 文本所在的行号是不一致的,所以我不能脱离这些,因此为什么我使用-A 20.
- 理想情况下我宁愿拥有它,所以当它在字符串后搜索时,它会在它停止时找到并继续搜索。

总结:找到4320101,在4320101(或一行空白行)上输出20行,然后输出4320101以下的所有行到

</eventUpdate>

进行研究我不确定如何让awk,nawk或sed对我有利于这样做。

6 个答案:

答案 0 :(得分:1)

这是一个丑陋的awk解决方案:)

awk 'BEGIN{last=1}
{if((length($0)==0) || (Random ~ $0))last=NR} 
/4320101/{flag=1;
if((NR-last)>20) last=NR-20;
cmd="sed -n \""last+1","NR-1"p \" input.txt";
system(cmd);
}
flag==1{print}
/eventUpdate/{flag=0}' <filename>

基本上它的作用是跟踪Random变量中包含last模式的最后一个空白行或行。现在,如果找到了4320101,则会从that line -20 or last打印出较近的system sed命令。并设置flagflag会导致打印下一行以上的行,直到找到eventUpdate为止。虽然没有测试,但应该正常工作

答案 1 :(得分:1)

sed / awk中的后视总是很棘手..这个自包含的awk脚本基本上保存了最后20行,当它到达4320101时它会打印这些存储的行,直到找到空白或不需要的线的点,然后停止。此时它会切换到printall模式并打印所有行,直到遇到eventUpdate,然后打印并退出。

awk '
function store( line ) {
    for( i=0; i <= 20; i++ ) {
        last[i-1] = last[i]; i++;
    };
    last[20]=line;
};
function purge() {
    for( i=20; i >= 0; i-- ) {
        if( length(last[i])==0 || last[i] ~ "Random" ) {
            stop=i;
            break
        };
    };
    for( i=(stop+1); i <= 20; i++ ) {
        print last[i];
    };

};
{
store($0);
if( /4320101/ ) {
    purge();
    printall=1;
    next;
};
if( printall == 1) {
    print;
    if( /eventUpdate/ ) {
        exit 0;
    };
};
}' test

答案 2 :(得分:1)

你可以尝试这样的事情 -

awk '{ 
    a[NR] = $0
}

/<\/eventUpdate>/ { 
    x = NR
}

END {
    for (i in a) {
        if (a[i]~/4320101/) {
            for (j=i-20;j<=x;j++) {
            print a[j]
            }
        }
    }
}' file

答案 3 :(得分:1)

让我们看看我是否理解您的要求:

您有两个字符串,我称之为KEYLIMIT。你要打印:

  1. 包含KEY的行前最多20行,但如果有空行则停止。

  2. 包含KEY的行与包含LIMIT的以下行之间的所有行。 (这忽略了你的要求,即不超过100条这样的线;如果这很重要,那么添加相对简单。)

  3. 完成(1)的最简单方法是保留20行的循环缓冲区,并在点击key时将其打印出来。 sed或awk中(2)是微不足道的,因为你可以使用双地址形式来打印范围。

    让我们在awk中做到这一点:

    #file: extract.awk
    
    # Initialize the circular buffer
    BEGIN          { count = 0; }
    # When we hit an empty line, clear the circular buffer
    length() == 0  { count = 0; next; }
    # When we hit `key`, print and clear the circular buffer
    index($0, KEY) { for (i = count < 20 ? 0 : count - 20; i < count; ++i)
                       print buf[i % 20];
                     hi = 0;
                   }
    # While we're between key and limit, print the line
    index($0, KEY),index($0, LIMIT)
                   { print; next; }
    # Otherwise, save the line
                   { buf[count++ % 20] = $0; }
    

    为了实现这一点,我们需要设置KEYLIMIT的值。我们可以在命令行上执行此操作:

    awk -v "KEY=4320101" -v "LIMIT=</eventUpdate>" -f extract.awk $FILENAME
    

    备注:

    1. 我使用index($0, foo)而不是更常见的/foo/,因为它避免了必须转义正则表达式特殊字符,并且在要求中没有任何地方甚至需要regexen。 index(haystack, needle)会在needle中返回haystack的索引,索引从1开始,如果找不到0则返回needle。用作真/假值,找到needle即可。

    2. next导致当前行的处理结束。它可以非常方便,正如这个小程序所示。

答案 4 :(得分:1)

这可能适合你(GNU sed):

sed ':a;s/\n/&/20;tb;$!{N;ba};:b;/4320102/!D;:c;n;/<\/eventUpdate>/!bc' file

编辑:

  • :a;s/\n/&/20;tb;$!{N;ba};这会在模式空间(PS)中保留20行的窗口
  • :b;/4320102!D;这会将上面的窗口移动到文件中,直到找到模式4320102
  • :c;n;/<\/eventUpdate>/!bc打印20行窗口以及任何后续行,直到找到模式<\/eventUpdate>

答案 5 :(得分:0)

最简单的方法是使用文件的2次传递 - 第一次用于识别找到目标正则表达式的范围内的行号,第二次用于打印所选范围内的行,例如:

awk '
NR==FNR {
    if ($0 ~ /\<4320101\>/ {
        for (i=NR-20;i<NR;i++)
            range[i]
        inRange = 1
    }
    if (inRange) {
        range[NR]
    }
    if ($0 ~ /<\/eventUpdate>/) {
        inRange = 0
    }
    next
}
FNR in range
' file file